Merge "Provisioning Network type constant"
diff --git a/Android.mk b/Android.mk
index 121a38e..e352bc7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -332,6 +332,7 @@
 	core/java/com/android/internal/os/IDropBoxManagerService.aidl \
 	core/java/com/android/internal/os/IParcelFileDescriptorFactory.aidl \
 	core/java/com/android/internal/os/IResultReceiver.aidl \
+	core/java/com/android/internal/os/IShellCallback.aidl \
 	core/java/com/android/internal/statusbar/IStatusBar.aidl \
 	core/java/com/android/internal/statusbar/IStatusBarService.aidl \
 	core/java/com/android/internal/textservice/ISpellCheckerService.aidl \
diff --git a/apct-tests/perftests/core/Android.mk b/apct-tests/perftests/core/Android.mk
index eb07c05..5fe2f02 100644
--- a/apct-tests/perftests/core/Android.mk
+++ b/apct-tests/perftests/core/Android.mk
@@ -12,5 +12,8 @@
 
 LOCAL_PACKAGE_NAME := CorePerfTests
 
+# Use google-fonts/dancing-script for the performance metrics
+LOCAL_ASSET_DIR := $(TOP)/external/google-fonts/dancing-script
+
 include $(BUILD_PACKAGE)
 
diff --git a/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
new file mode 100644
index 0000000..11ee599
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/graphics/perftests/TypefaceCreatePerfTest.java
@@ -0,0 +1,106 @@
+/*
+ * 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 android.graphics.perftests;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.graphics.Typeface;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TypefaceCreatePerfTest {
+    // A font file name in asset directory.
+    private static final String TEST_FONT_NAME = "DancingScript-Regular.ttf";
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void testCreate_fromFamily() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        while (state.keepRunning()) {
+            Typeface face = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
+        }
+    }
+
+    @Test
+    public void testCreate_fromFamilyName() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        while (state.keepRunning()) {
+            Typeface face = Typeface.create("monospace", Typeface.NORMAL);
+        }
+    }
+
+    @Test
+    public void testCreate_fromAsset() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final Context context = InstrumentationRegistry.getContext();
+        final AssetManager am = context.getAssets();
+
+        while (state.keepRunning()) {
+            Typeface face = Typeface.createFromAsset(am, TEST_FONT_NAME);
+        }
+    }
+
+    @Test
+    public void testCreate_fromFile() {
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        final Context context = InstrumentationRegistry.getContext();
+        final AssetManager am = context.getAssets();
+
+        File outFile = null;
+        try {
+            outFile = File.createTempFile("example", "ttf", context.getCacheDir());
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        try (InputStream in = am.open(TEST_FONT_NAME);
+                OutputStream out = new FileOutputStream(outFile)) {
+            byte[] buf = new byte[1024];
+            int n = 0;
+            while ((n = in.read(buf)) != -1) {
+                out.write(buf, 0, n);
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+
+        while (state.keepRunning()) {
+            Typeface face = Typeface.createFromFile(outFile);
+        }
+
+        outFile.delete();
+    }
+}
diff --git a/api/current.txt b/api/current.txt
index 2d3fde8..0a8965f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4042,6 +4042,7 @@
     field public static final java.lang.String OPSTR_MOCK_LOCATION = "android:mock_location";
     field public static final java.lang.String OPSTR_MONITOR_HIGH_POWER_LOCATION = "android:monitor_location_high_power";
     field public static final java.lang.String OPSTR_MONITOR_LOCATION = "android:monitor_location";
+    field public static final java.lang.String OPSTR_PROCESS_OUTGOING_CALLS = "android:process_outgoing_calls";
     field public static final java.lang.String OPSTR_READ_CALENDAR = "android:read_calendar";
     field public static final java.lang.String OPSTR_READ_CALL_LOG = "android:read_call_log";
     field public static final java.lang.String OPSTR_READ_CELL_BROADCASTS = "android:read_cell_broadcasts";
@@ -21461,6 +21462,7 @@
     field public static final int AMR_NB = 3; // 0x3
     field public static final int AMR_WB = 4; // 0x4
     field public static final int DEFAULT = 0; // 0x0
+    field public static final int MPEG_2_TS = 8; // 0x8
     field public static final int MPEG_4 = 2; // 0x2
     field public static final deprecated int RAW_AMR = 3; // 0x3
     field public static final int THREE_GPP = 1; // 0x1
@@ -30305,6 +30307,8 @@
 
   public final class PrintJobInfo implements android.os.Parcelable {
     method public int describeContents();
+    method public int getAdvancedIntOption(java.lang.String);
+    method public java.lang.String getAdvancedStringOption(java.lang.String);
     method public android.print.PrintAttributes getAttributes();
     method public int getCopies();
     method public long getCreationTime();
@@ -30313,6 +30317,7 @@
     method public android.print.PageRange[] getPages();
     method public android.print.PrinterId getPrinterId();
     method public int getState();
+    method public boolean hasAdvancedOption(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.print.PrintJobInfo> CREATOR;
     field public static final int STATE_BLOCKED = 4; // 0x4
@@ -36915,6 +36920,7 @@
     field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
     field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
     field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
+    field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
     field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
     field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
     field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
@@ -45843,6 +45849,8 @@
     method public android.webkit.WebSettings getSettings();
     method public java.lang.String getTitle();
     method public java.lang.String getUrl();
+    method public android.webkit.WebChromeClient getWebChromeClient();
+    method public android.webkit.WebViewClient getWebViewClient();
     method public void goBack();
     method public void goBackOrForward(int);
     method public void goForward();
@@ -52070,6 +52078,8 @@
     enum_constant public static final java.lang.annotation.ElementType PACKAGE;
     enum_constant public static final java.lang.annotation.ElementType PARAMETER;
     enum_constant public static final java.lang.annotation.ElementType TYPE;
+    enum_constant public static final java.lang.annotation.ElementType TYPE_PARAMETER;
+    enum_constant public static final java.lang.annotation.ElementType TYPE_USE;
   }
 
   public class IncompleteAnnotationException extends java.lang.RuntimeException {
@@ -52151,7 +52161,7 @@
     method public abstract <T extends java.lang.annotation.Annotation> T getAnnotation(java.lang.Class<T>);
     method public abstract java.lang.annotation.Annotation[] getAnnotations();
     method public default <T extends java.lang.annotation.Annotation> T[] getAnnotationsByType(java.lang.Class<T>);
-    method public default <T extends java.lang.annotation.Annotation> java.lang.annotation.Annotation getDeclaredAnnotation(java.lang.Class<T>);
+    method public default <T extends java.lang.annotation.Annotation> T getDeclaredAnnotation(java.lang.Class<T>);
     method public abstract java.lang.annotation.Annotation[] getDeclaredAnnotations();
     method public default <T extends java.lang.annotation.Annotation> T[] getDeclaredAnnotationsByType(java.lang.Class<T>);
     method public default boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
@@ -53276,8 +53286,6 @@
     method public java.net.URLConnection openConnection(java.net.Proxy) throws java.io.IOException;
     method public final java.io.InputStream openStream() throws java.io.IOException;
     method public boolean sameFile(java.net.URL);
-    method protected void set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String);
-    method protected void set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     method public static void setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory);
     method public java.lang.String toExternalForm();
     method public java.net.URI toURI() throws java.net.URISyntaxException;
@@ -59420,8 +59428,10 @@
     method public java.util.Set<java.lang.String> getUnicodeLocaleKeys();
     method public java.lang.String getUnicodeLocaleType(java.lang.String);
     method public java.lang.String getVariant();
+    method public boolean hasExtensions();
     method public static synchronized void setDefault(java.util.Locale);
     method public static synchronized void setDefault(java.util.Locale.Category, java.util.Locale);
+    method public java.util.Locale stripExtensions();
     method public java.lang.String toLanguageTag();
     method public final java.lang.String toString();
     field public static final java.util.Locale CANADA;
diff --git a/api/system-current.txt b/api/system-current.txt
index d0116ee..2b5c33d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4172,6 +4172,7 @@
     field public static final java.lang.String OPSTR_MOCK_LOCATION = "android:mock_location";
     field public static final java.lang.String OPSTR_MONITOR_HIGH_POWER_LOCATION = "android:monitor_location_high_power";
     field public static final java.lang.String OPSTR_MONITOR_LOCATION = "android:monitor_location";
+    field public static final java.lang.String OPSTR_PROCESS_OUTGOING_CALLS = "android:process_outgoing_calls";
     field public static final java.lang.String OPSTR_READ_CALENDAR = "android:read_calendar";
     field public static final java.lang.String OPSTR_READ_CALL_LOG = "android:read_call_log";
     field public static final java.lang.String OPSTR_READ_CELL_BROADCASTS = "android:read_cell_broadcasts";
@@ -22993,6 +22994,7 @@
     field public static final int AMR_NB = 3; // 0x3
     field public static final int AMR_WB = 4; // 0x4
     field public static final int DEFAULT = 0; // 0x0
+    field public static final int MPEG_2_TS = 8; // 0x8
     field public static final int MPEG_4 = 2; // 0x2
     field public static final deprecated int RAW_AMR = 3; // 0x3
     field public static final int THREE_GPP = 1; // 0x1
@@ -32905,6 +32907,8 @@
 
   public final class PrintJobInfo implements android.os.Parcelable {
     method public int describeContents();
+    method public int getAdvancedIntOption(java.lang.String);
+    method public java.lang.String getAdvancedStringOption(java.lang.String);
     method public android.print.PrintAttributes getAttributes();
     method public int getCopies();
     method public long getCreationTime();
@@ -32913,6 +32917,7 @@
     method public android.print.PageRange[] getPages();
     method public android.print.PrinterId getPrinterId();
     method public int getState();
+    method public boolean hasAdvancedOption(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.print.PrintJobInfo> CREATOR;
     field public static final int STATE_BLOCKED = 4; // 0x4
@@ -40006,6 +40011,7 @@
     field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
     field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
     field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
+    field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
     field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
     field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
     field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
@@ -49119,6 +49125,8 @@
     method public android.webkit.WebSettings getSettings();
     method public java.lang.String getTitle();
     method public java.lang.String getUrl();
+    method public android.webkit.WebChromeClient getWebChromeClient();
+    method public android.webkit.WebViewClient getWebViewClient();
     method public android.webkit.WebViewProvider getWebViewProvider();
     method public void goBack();
     method public void goBackOrForward(int);
@@ -49399,6 +49407,8 @@
     method public abstract java.lang.String getUrl();
     method public abstract android.webkit.WebViewProvider.ViewDelegate getViewDelegate();
     method public abstract int getVisibleTitleHeight();
+    method public abstract android.webkit.WebChromeClient getWebChromeClient();
+    method public abstract android.webkit.WebViewClient getWebViewClient();
     method public abstract android.view.View getZoomControls();
     method public abstract void goBack();
     method public abstract void goBackOrForward(int);
@@ -55607,6 +55617,8 @@
     enum_constant public static final java.lang.annotation.ElementType PACKAGE;
     enum_constant public static final java.lang.annotation.ElementType PARAMETER;
     enum_constant public static final java.lang.annotation.ElementType TYPE;
+    enum_constant public static final java.lang.annotation.ElementType TYPE_PARAMETER;
+    enum_constant public static final java.lang.annotation.ElementType TYPE_USE;
   }
 
   public class IncompleteAnnotationException extends java.lang.RuntimeException {
@@ -55688,7 +55700,7 @@
     method public abstract <T extends java.lang.annotation.Annotation> T getAnnotation(java.lang.Class<T>);
     method public abstract java.lang.annotation.Annotation[] getAnnotations();
     method public default <T extends java.lang.annotation.Annotation> T[] getAnnotationsByType(java.lang.Class<T>);
-    method public default <T extends java.lang.annotation.Annotation> java.lang.annotation.Annotation getDeclaredAnnotation(java.lang.Class<T>);
+    method public default <T extends java.lang.annotation.Annotation> T getDeclaredAnnotation(java.lang.Class<T>);
     method public abstract java.lang.annotation.Annotation[] getDeclaredAnnotations();
     method public default <T extends java.lang.annotation.Annotation> T[] getDeclaredAnnotationsByType(java.lang.Class<T>);
     method public default boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
@@ -56813,8 +56825,6 @@
     method public java.net.URLConnection openConnection(java.net.Proxy) throws java.io.IOException;
     method public final java.io.InputStream openStream() throws java.io.IOException;
     method public boolean sameFile(java.net.URL);
-    method protected void set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String);
-    method protected void set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     method public static void setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory);
     method public java.lang.String toExternalForm();
     method public java.net.URI toURI() throws java.net.URISyntaxException;
@@ -62957,8 +62967,10 @@
     method public java.util.Set<java.lang.String> getUnicodeLocaleKeys();
     method public java.lang.String getUnicodeLocaleType(java.lang.String);
     method public java.lang.String getVariant();
+    method public boolean hasExtensions();
     method public static synchronized void setDefault(java.util.Locale);
     method public static synchronized void setDefault(java.util.Locale.Category, java.util.Locale);
+    method public java.util.Locale stripExtensions();
     method public java.lang.String toLanguageTag();
     method public final java.lang.String toString();
     field public static final java.util.Locale CANADA;
diff --git a/api/test-current.txt b/api/test-current.txt
index 9c744c5..4de2c61 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -4045,6 +4045,7 @@
     field public static final java.lang.String OPSTR_MOCK_LOCATION = "android:mock_location";
     field public static final java.lang.String OPSTR_MONITOR_HIGH_POWER_LOCATION = "android:monitor_location_high_power";
     field public static final java.lang.String OPSTR_MONITOR_LOCATION = "android:monitor_location";
+    field public static final java.lang.String OPSTR_PROCESS_OUTGOING_CALLS = "android:process_outgoing_calls";
     field public static final java.lang.String OPSTR_READ_CALENDAR = "android:read_calendar";
     field public static final java.lang.String OPSTR_READ_CALL_LOG = "android:read_call_log";
     field public static final java.lang.String OPSTR_READ_CELL_BROADCASTS = "android:read_cell_broadcasts";
@@ -21534,6 +21535,7 @@
     field public static final int AMR_NB = 3; // 0x3
     field public static final int AMR_WB = 4; // 0x4
     field public static final int DEFAULT = 0; // 0x0
+    field public static final int MPEG_2_TS = 8; // 0x8
     field public static final int MPEG_4 = 2; // 0x2
     field public static final deprecated int RAW_AMR = 3; // 0x3
     field public static final int THREE_GPP = 1; // 0x1
@@ -30380,6 +30382,8 @@
 
   public final class PrintJobInfo implements android.os.Parcelable {
     method public int describeContents();
+    method public int getAdvancedIntOption(java.lang.String);
+    method public java.lang.String getAdvancedStringOption(java.lang.String);
     method public android.print.PrintAttributes getAttributes();
     method public int getCopies();
     method public long getCreationTime();
@@ -30390,6 +30394,7 @@
     method public float getProgress();
     method public int getState();
     method public java.lang.CharSequence getStatus(android.content.pm.PackageManager);
+    method public boolean hasAdvancedOption(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.print.PrintJobInfo> CREATOR;
     field public static final int STATE_BLOCKED = 4; // 0x4
@@ -36996,6 +37001,7 @@
     field public static final java.lang.String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
     field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
     field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
+    field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
     field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
     field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
     field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
@@ -45931,6 +45937,8 @@
     method public android.webkit.WebSettings getSettings();
     method public java.lang.String getTitle();
     method public java.lang.String getUrl();
+    method public android.webkit.WebChromeClient getWebChromeClient();
+    method public android.webkit.WebViewClient getWebViewClient();
     method public void goBack();
     method public void goBackOrForward(int);
     method public void goForward();
@@ -52172,6 +52180,8 @@
     enum_constant public static final java.lang.annotation.ElementType PACKAGE;
     enum_constant public static final java.lang.annotation.ElementType PARAMETER;
     enum_constant public static final java.lang.annotation.ElementType TYPE;
+    enum_constant public static final java.lang.annotation.ElementType TYPE_PARAMETER;
+    enum_constant public static final java.lang.annotation.ElementType TYPE_USE;
   }
 
   public class IncompleteAnnotationException extends java.lang.RuntimeException {
@@ -52253,7 +52263,7 @@
     method public abstract <T extends java.lang.annotation.Annotation> T getAnnotation(java.lang.Class<T>);
     method public abstract java.lang.annotation.Annotation[] getAnnotations();
     method public default <T extends java.lang.annotation.Annotation> T[] getAnnotationsByType(java.lang.Class<T>);
-    method public default <T extends java.lang.annotation.Annotation> java.lang.annotation.Annotation getDeclaredAnnotation(java.lang.Class<T>);
+    method public default <T extends java.lang.annotation.Annotation> T getDeclaredAnnotation(java.lang.Class<T>);
     method public abstract java.lang.annotation.Annotation[] getDeclaredAnnotations();
     method public default <T extends java.lang.annotation.Annotation> T[] getDeclaredAnnotationsByType(java.lang.Class<T>);
     method public default boolean isAnnotationPresent(java.lang.Class<? extends java.lang.annotation.Annotation>);
@@ -53378,8 +53388,6 @@
     method public java.net.URLConnection openConnection(java.net.Proxy) throws java.io.IOException;
     method public final java.io.InputStream openStream() throws java.io.IOException;
     method public boolean sameFile(java.net.URL);
-    method protected void set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String);
-    method protected void set(java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
     method public static void setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory);
     method public java.lang.String toExternalForm();
     method public java.net.URI toURI() throws java.net.URISyntaxException;
@@ -59522,8 +59530,10 @@
     method public java.util.Set<java.lang.String> getUnicodeLocaleKeys();
     method public java.lang.String getUnicodeLocaleType(java.lang.String);
     method public java.lang.String getVariant();
+    method public boolean hasExtensions();
     method public static synchronized void setDefault(java.util.Locale);
     method public static synchronized void setDefault(java.util.Locale.Category, java.util.Locale);
+    method public java.util.Locale stripExtensions();
     method public java.lang.String toLanguageTag();
     method public final java.lang.String toString();
     field public static final java.util.Locale CANADA;
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 91334bd..3759de2 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -26,7 +26,6 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityManagerNative;
-import android.app.ActivityOptions;
 import android.app.IActivityContainer;
 import android.app.IActivityController;
 import android.app.IActivityManager;
@@ -46,7 +45,6 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.ParceledListSlice;
-import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -55,10 +53,11 @@
 import android.os.Bundle;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.ResultReceiver;
 import android.os.SELinux;
 import android.os.ServiceManager;
+import android.os.ShellCallback;
 import android.os.ShellCommand;
-import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.text.TextUtils;
@@ -72,6 +71,7 @@
 
 import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileDescriptor;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStreamReader;
@@ -96,6 +96,7 @@
     // Amount we reduce the stack size by when testing a task re-size.
     private static final int STACK_BOUNDS_INSET = 10;
 
+    public static final String NO_CLASS_ERROR_CODE = "Error type 3";
     private IActivityManager mAm;
     private IPackageManager mPm;
 
@@ -198,7 +199,7 @@
                 "    --track-allocation: enable tracking of object allocations\n" +
                 "    --user <USER_ID> | current: Specify which user to run as; if not\n" +
                 "        specified then run as the current user.\n" +
-                "    --stack <STACK_ID>: Specify into which stack should the activity be put." +
+                "    --stack <STACK_ID>: Specify into which stack should the activity be put.\n" +
                 "\n" +
                 "am startservice: start a Service.  Options are:\n" +
                 "    --user <USER_ID> | current: Specify which user to run as; if not\n" +
@@ -385,17 +386,13 @@
         String op = nextArgRequired();
 
         if (op.equals("start")) {
-            runStart();
+            runAmCmd(getRawArgs());
         } else if (op.equals("startservice")) {
             runStartService();
         } else if (op.equals("stopservice")) {
             runStopService();
-        } else if (op.equals("force-stop")) {
-            runForceStop();
-        } else if (op.equals("kill")) {
-            runKill();
-        } else if (op.equals("kill-all")) {
-            runKillAll();
+        } else if (op.equals("force-stop") || op.equals("kill") || op.equals("kill-all")) {
+            runAmCmd(getRawArgs());
         } else if (op.equals("instrument")) {
             runInstrument();
         } else if (op.equals("trace-ipc")) {
@@ -475,6 +472,49 @@
         return userId;
     }
 
+    static final class MyShellCallback extends ShellCallback {
+        @Override public ParcelFileDescriptor onOpenOutputFile(String path, String seLinuxContext) {
+            File file = new File(path);
+            //System.err.println("Opening file: " + file.getAbsolutePath());
+            //Log.i("Am", "Opening file: " + file.getAbsolutePath());
+            final ParcelFileDescriptor fd;
+            try {
+                fd = ParcelFileDescriptor.open(file,
+                        ParcelFileDescriptor.MODE_CREATE |
+                        ParcelFileDescriptor.MODE_TRUNCATE |
+                        ParcelFileDescriptor.MODE_WRITE_ONLY);
+            } catch (FileNotFoundException e) {
+                String msg = "Unable to open file " + path + ": " + e;
+                System.err.println(msg);
+                throw new IllegalArgumentException(msg);
+            }
+            if (seLinuxContext != null) {
+                final String tcon = SELinux.getFileContext(file.getAbsolutePath());
+                if (!SELinux.checkSELinuxAccess(seLinuxContext, tcon, "file", "write")) {
+                    try {
+                        fd.close();
+                    } catch (IOException e) {
+                    }
+                    String msg = "System server has no access to file context " + tcon;
+                    System.err.println(msg + " (from path " + file.getAbsolutePath()
+                            + ", context " + seLinuxContext + ")");
+                    throw new IllegalArgumentException(msg);
+                }
+            }
+            return fd;
+        }
+    }
+
+    void runAmCmd(String[] args) throws AndroidException {
+        try {
+            mAm.asBinder().shellCommand(FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
+                    args, new MyShellCallback(), new ResultReceiver(null) { });
+        } catch (RemoteException e) {
+            System.err.println(NO_SYSTEM_ERROR_CODE);
+            throw new AndroidException("Can't call activity manager; is the system running?");
+        }
+    }
+
     private Intent makeIntent(int defUser) throws URISyntaxException {
         mStartFlags = 0;
         mWaitOption = false;
@@ -558,211 +598,6 @@
         }
     }
 
-    private void runStart() throws Exception {
-        Intent intent = makeIntent(UserHandle.USER_CURRENT);
-
-        if (mUserId == UserHandle.USER_ALL) {
-            System.err.println("Error: Can't start service with user 'all'");
-            return;
-        }
-
-        String mimeType = intent.getType();
-        if (mimeType == null && intent.getData() != null
-                && "content".equals(intent.getData().getScheme())) {
-            mimeType = mAm.getProviderMimeType(intent.getData(), mUserId);
-        }
-
-
-        do {
-            if (mStopOption) {
-                String packageName;
-                if (intent.getComponent() != null) {
-                    packageName = intent.getComponent().getPackageName();
-                } else {
-                    List<ResolveInfo> activities = mPm.queryIntentActivities(intent, mimeType, 0,
-                            mUserId).getList();
-                    if (activities == null || activities.size() <= 0) {
-                        System.err.println("Error: Intent does not match any activities: "
-                                + intent);
-                        return;
-                    } else if (activities.size() > 1) {
-                        System.err.println("Error: Intent matches multiple activities; can't stop: "
-                                + intent);
-                        return;
-                    }
-                    packageName = activities.get(0).activityInfo.packageName;
-                }
-                System.out.println("Stopping: " + packageName);
-                mAm.forceStopPackage(packageName, mUserId);
-                Thread.sleep(250);
-            }
-
-            System.out.println("Starting: " + intent);
-            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
-            ParcelFileDescriptor fd = null;
-            ProfilerInfo profilerInfo = null;
-
-            if (mProfileFile != null) {
-                try {
-                    fd = openForSystemServer(
-                            new File(mProfileFile),
-                            ParcelFileDescriptor.MODE_CREATE |
-                            ParcelFileDescriptor.MODE_TRUNCATE |
-                            ParcelFileDescriptor.MODE_WRITE_ONLY);
-                } catch (FileNotFoundException e) {
-                    System.err.println("Error: Unable to open file: " + mProfileFile);
-                    System.err.println("Consider using a file under /data/local/tmp/");
-                    return;
-                }
-                profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop);
-            }
-
-            IActivityManager.WaitResult result = null;
-            int res;
-            final long startTime = SystemClock.uptimeMillis();
-            ActivityOptions options = null;
-            if (mStackId != INVALID_STACK_ID) {
-                options = ActivityOptions.makeBasic();
-                options.setLaunchStackId(mStackId);
-            }
-            if (mWaitOption) {
-                result = mAm.startActivityAndWait(null, null, intent, mimeType,
-                        null, null, 0, mStartFlags, profilerInfo,
-                        options != null ? options.toBundle() : null, mUserId);
-                res = result.result;
-            } else {
-                res = mAm.startActivityAsUser(null, null, intent, mimeType,
-                        null, null, 0, mStartFlags, profilerInfo,
-                        options != null ? options.toBundle() : null, mUserId);
-            }
-            final long endTime = SystemClock.uptimeMillis();
-            PrintStream out = mWaitOption ? System.out : System.err;
-            boolean launched = false;
-            switch (res) {
-                case ActivityManager.START_SUCCESS:
-                    launched = true;
-                    break;
-                case ActivityManager.START_SWITCHES_CANCELED:
-                    launched = true;
-                    out.println(
-                            "Warning: Activity not started because the "
-                            + " current activity is being kept for the user.");
-                    break;
-                case ActivityManager.START_DELIVERED_TO_TOP:
-                    launched = true;
-                    out.println(
-                            "Warning: Activity not started, intent has "
-                            + "been delivered to currently running "
-                            + "top-most instance.");
-                    break;
-                case ActivityManager.START_RETURN_INTENT_TO_CALLER:
-                    launched = true;
-                    out.println(
-                            "Warning: Activity not started because intent "
-                            + "should be handled by the caller");
-                    break;
-                case ActivityManager.START_TASK_TO_FRONT:
-                    launched = true;
-                    out.println(
-                            "Warning: Activity not started, its current "
-                            + "task has been brought to the front");
-                    break;
-                case ActivityManager.START_INTENT_NOT_RESOLVED:
-                    out.println(
-                            "Error: Activity not started, unable to "
-                            + "resolve " + intent.toString());
-                    break;
-                case ActivityManager.START_CLASS_NOT_FOUND:
-                    out.println(NO_CLASS_ERROR_CODE);
-                    out.println("Error: Activity class " +
-                            intent.getComponent().toShortString()
-                            + " does not exist.");
-                    break;
-                case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
-                    out.println(
-                            "Error: Activity not started, you requested to "
-                            + "both forward and receive its result");
-                    break;
-                case ActivityManager.START_PERMISSION_DENIED:
-                    out.println(
-                            "Error: Activity not started, you do not "
-                            + "have permission to access it.");
-                    break;
-                case ActivityManager.START_NOT_VOICE_COMPATIBLE:
-                    out.println(
-                            "Error: Activity not started, voice control not allowed for: "
-                                    + intent);
-                    break;
-                case ActivityManager.START_NOT_CURRENT_USER_ACTIVITY:
-                    out.println(
-                            "Error: Not allowed to start background user activity"
-                            + " that shouldn't be displayed for all users.");
-                    break;
-                default:
-                    out.println(
-                            "Error: Activity not started, unknown error code " + res);
-                    break;
-            }
-            if (mWaitOption && launched) {
-                if (result == null) {
-                    result = new IActivityManager.WaitResult();
-                    result.who = intent.getComponent();
-                }
-                System.out.println("Status: " + (result.timeout ? "timeout" : "ok"));
-                if (result.who != null) {
-                    System.out.println("Activity: " + result.who.flattenToShortString());
-                }
-                if (result.thisTime >= 0) {
-                    System.out.println("ThisTime: " + result.thisTime);
-                }
-                if (result.totalTime >= 0) {
-                    System.out.println("TotalTime: " + result.totalTime);
-                }
-                System.out.println("WaitTime: " + (endTime-startTime));
-                System.out.println("Complete");
-            }
-            mRepeat--;
-            if (mRepeat > 0) {
-                mAm.unhandledBack();
-            }
-        } while (mRepeat > 0);
-    }
-
-    private void runForceStop() throws Exception {
-        int userId = UserHandle.USER_ALL;
-
-        String opt;
-        while ((opt=nextOption()) != null) {
-            if (opt.equals("--user")) {
-                userId = parseUserArg(nextArgRequired());
-            } else {
-                System.err.println("Error: Unknown option: " + opt);
-                return;
-            }
-        }
-        mAm.forceStopPackage(nextArgRequired(), userId);
-    }
-
-    private void runKill() throws Exception {
-        int userId = UserHandle.USER_ALL;
-
-        String opt;
-        while ((opt=nextOption()) != null) {
-            if (opt.equals("--user")) {
-                userId = parseUserArg(nextArgRequired());
-            } else {
-                System.err.println("Error: Unknown option: " + opt);
-                return;
-            }
-        }
-        mAm.killBackgroundProcesses(nextArgRequired(), userId);
-    }
-
-    private void runKillAll() throws Exception {
-        mAm.killAllBackgroundProcesses();
-    }
-
     private void sendBroadcast() throws Exception {
         Intent intent = makeIntent(UserHandle.USER_CURRENT);
         IntentReceiver receiver = new IntentReceiver();
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 32a8088..ace4e32 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -49,9 +49,12 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IUserManager;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.SELinux;
 import android.os.ServiceManager;
+import android.os.ShellCallback;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -68,6 +71,7 @@
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -284,13 +288,45 @@
         }
     }
 
+    static final class MyShellCallback extends ShellCallback {
+        @Override public ParcelFileDescriptor onOpenOutputFile(String path, String seLinuxContext) {
+            File file = new File(path);
+            final ParcelFileDescriptor fd;
+            try {
+                fd = ParcelFileDescriptor.open(file,
+                            ParcelFileDescriptor.MODE_CREATE |
+                            ParcelFileDescriptor.MODE_TRUNCATE |
+                            ParcelFileDescriptor.MODE_WRITE_ONLY);
+            } catch (FileNotFoundException e) {
+                String msg = "Unable to open file " + path + ": " + e;
+                System.err.println(msg);
+                throw new IllegalArgumentException(msg);
+            }
+            if (seLinuxContext != null) {
+                final String tcon = SELinux.getFileContext(file.getAbsolutePath());
+                if (!SELinux.checkSELinuxAccess(seLinuxContext, tcon, "file", "write")) {
+                    try {
+                        fd.close();
+                    } catch (IOException e) {
+                    }
+                    String msg = "System server has no access to file context " + tcon;
+                    System.err.println(msg + " (from path " + file.getAbsolutePath()
+                            + ", context " + seLinuxContext + ")");
+                    throw new IllegalArgumentException(msg);
+                }
+            }
+            return fd;
+        }
+    }
+
     private int runShellCommand(String serviceName, String[] args) {
         final HandlerThread handlerThread = new HandlerThread("results");
         handlerThread.start();
         try {
             ServiceManager.getService(serviceName).shellCommand(
                     FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
-                    args, new ResultReceiver(new Handler(handlerThread.getLooper())));
+                    args, new MyShellCallback(),
+                    new ResultReceiver(new Handler(handlerThread.getLooper())));
             return 0;
         } catch (RemoteException e) {
             e.printStackTrace();
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 5a9498f..191cc49 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -311,9 +311,7 @@
     /** Access APIs for SIP calling over VOIP or WiFi */
     public static final String OPSTR_USE_SIP
             = "android:use_sip";
-    /** Access APIs for diverting outgoing calls
-     * @hide
-     */
+    /** Access APIs for diverting outgoing calls */
     public static final String OPSTR_PROCESS_OUTGOING_CALLS
             = "android:process_outgoing_calls";
     /** Use the fingerprint API. */
diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java
index f33af39..e4a22c4 100644
--- a/core/java/android/app/IntentService.java
+++ b/core/java/android/app/IntentService.java
@@ -46,7 +46,8 @@
  * <div class="special reference">
  * <h3>Developer Guides</h3>
  * <p>For a detailed discussion about how to create services, read the
- * <a href="{@docRoot}guide/topics/fundamentals/services.html">Services</a> developer guide.</p>
+ * <a href="{@docRoot}guide/components/services.html">Services</a> developer
+ * guide.</p>
  * </div>
  *
  * @see android.os.AsyncTask
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 74f48c6..02c8f0c 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3037,6 +3037,18 @@
             "android.intent.action.MANAGED_PROFILE_UNAVAILABLE";
 
     /**
+     * Broadcast sent to the system user when the 'device locked' state changes for any user.
+     * Carries an extra {@link #EXTRA_USER_HANDLE} that specifies the ID of the user for which
+     * the device was locked or unlocked.
+     *
+     * This is only sent to registered receivers.
+     *
+     * @hide
+     */
+    public static final String ACTION_DEVICE_LOCKED_CHANGED =
+            "android.intent.action.DEVICE_LOCKED_CHANGED";
+
+    /**
      * Sent when the user taps on the clock widget in the system's "quick settings" area.
      */
     public static final String ACTION_QUICK_CLOCK =
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index ea8ba2f..cf77567 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -23,7 +23,6 @@
 
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
 import java.lang.reflect.Modifier;
@@ -361,13 +360,14 @@
             ParcelFileDescriptor out = data.readFileDescriptor();
             ParcelFileDescriptor err = data.readFileDescriptor();
             String[] args = data.readStringArray();
+            ShellCallback shellCallback = ShellCallback.CREATOR.createFromParcel(data);
             ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(data);
             try {
                 if (out != null) {
                     shellCommand(in != null ? in.getFileDescriptor() : null,
                             out.getFileDescriptor(),
                             err != null ? err.getFileDescriptor() : out.getFileDescriptor(),
-                            args, resultReceiver);
+                            args, shellCallback, resultReceiver);
                 }
             } finally {
                 IoUtils.closeQuietly(in);
@@ -459,13 +459,15 @@
      * @param out The raw file descriptor that normal command messages should be written to.
      * @param err The raw file descriptor that command error messages should be written to.
      * @param args Command-line arguments.
+     * @param callback Callback through which to interact with the invoking shell.
      * @param resultReceiver Called when the command has finished executing, with the result code.
      * @throws RemoteException
      * @hide
      */
     public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
-            String[] args, ResultReceiver resultReceiver) throws RemoteException {
-        onShellCommand(in, out, err, args, resultReceiver);
+            String[] args, ShellCallback callback,
+            ResultReceiver resultReceiver) throws RemoteException {
+        onShellCommand(in, out, err, args, callback, resultReceiver);
     }
 
     /**
@@ -477,7 +479,7 @@
      * @hide
      */
     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
-            String[] args, ResultReceiver resultReceiver) throws RemoteException {
+            String[] args, ShellCallback callback, ResultReceiver resultReceiver) throws RemoteException {
         FileOutputStream fout = new FileOutputStream(err != null ? err : out);
         PrintWriter pw = new FastPrintWriter(fout);
         pw.println("No shell command implementation.");
@@ -650,13 +652,15 @@
     }
 
     public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
-            String[] args, ResultReceiver resultReceiver) throws RemoteException {
+            String[] args, ShellCallback callback,
+            ResultReceiver resultReceiver) throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeFileDescriptor(in);
         data.writeFileDescriptor(out);
         data.writeFileDescriptor(err);
         data.writeStringArray(args);
+        ShellCallback.writeToParcel(callback, data);
         resultReceiver.writeToParcel(data, 0);
         try {
             transact(SHELL_COMMAND_TRANSACTION, data, reply, 0);
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 0fa8750..f762a05 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -220,11 +220,13 @@
      * @param out The raw file descriptor that normal command messages should be written to.
      * @param err The raw file descriptor that command error messages should be written to.
      * @param args Command-line arguments.
+     * @param shellCallback Optional callback to the caller's shell to perform operations in it.
      * @param resultReceiver Called when the command has finished executing, with the result code.
      * @hide
      */
     public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
-            String[] args, ResultReceiver resultReceiver) throws RemoteException;
+            String[] args, ShellCallback shellCallback,
+            ResultReceiver resultReceiver) throws RemoteException;
 
     /**
      * Perform a generic operation with the object.
diff --git a/core/java/android/os/IRecoverySystem.aidl b/core/java/android/os/IRecoverySystem.aidl
index 12830a4..c5ceecd 100644
--- a/core/java/android/os/IRecoverySystem.aidl
+++ b/core/java/android/os/IRecoverySystem.aidl
@@ -25,4 +25,5 @@
     boolean uncrypt(in String packageFile, IRecoverySystemProgressListener listener);
     boolean setupBcb(in String command);
     boolean clearBcb();
+    void rebootRecoveryWithCommand(in String command);
 }
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index f6e6ad6..85f999b 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -46,6 +46,7 @@
 import java.util.Map;
 import java.util.Set;
 
+import dalvik.annotation.optimization.FastNative;
 import dalvik.system.VMRuntime;
 
 /**
@@ -254,22 +255,35 @@
     // see libbinder's binder/Status.h
     private static final int EX_TRANSACTION_FAILED = -129;
 
+    @FastNative
     private static native int nativeDataSize(long nativePtr);
+    @FastNative
     private static native int nativeDataAvail(long nativePtr);
+    @FastNative
     private static native int nativeDataPosition(long nativePtr);
+    @FastNative
     private static native int nativeDataCapacity(long nativePtr);
+    @FastNative
     private static native long nativeSetDataSize(long nativePtr, int size);
+    @FastNative
     private static native void nativeSetDataPosition(long nativePtr, int pos);
+    @FastNative
     private static native void nativeSetDataCapacity(long nativePtr, int size);
 
+    @FastNative
     private static native boolean nativePushAllowFds(long nativePtr, boolean allowFds);
+    @FastNative
     private static native void nativeRestoreAllowFds(long nativePtr, boolean lastValue);
 
     private static native void nativeWriteByteArray(long nativePtr, byte[] b, int offset, int len);
     private static native void nativeWriteBlob(long nativePtr, byte[] b, int offset, int len);
+    @FastNative
     private static native void nativeWriteInt(long nativePtr, int val);
+    @FastNative
     private static native void nativeWriteLong(long nativePtr, long val);
+    @FastNative
     private static native void nativeWriteFloat(long nativePtr, float val);
+    @FastNative
     private static native void nativeWriteDouble(long nativePtr, double val);
     private static native void nativeWriteString(long nativePtr, String val);
     private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
@@ -277,9 +291,13 @@
 
     private static native byte[] nativeCreateByteArray(long nativePtr);
     private static native byte[] nativeReadBlob(long nativePtr);
+    @FastNative
     private static native int nativeReadInt(long nativePtr);
+    @FastNative
     private static native long nativeReadLong(long nativePtr);
+    @FastNative
     private static native float nativeReadFloat(long nativePtr);
+    @FastNative
     private static native double nativeReadDouble(long nativePtr);
     private static native String nativeReadString(long nativePtr);
     private static native IBinder nativeReadStrongBinder(long nativePtr);
@@ -294,6 +312,7 @@
             long nativePtr, byte[] data, int offset, int length);
     private static native long nativeAppendFrom(
             long thisNativePtr, long otherNativePtr, int offset, int length);
+    @FastNative
     private static native boolean nativeHasFileDescriptors(long nativePtr);
     private static native void nativeWriteInterfaceToken(long nativePtr, String interfaceName);
     private static native void nativeEnforceInterface(long nativePtr, String interfaceName);
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 0b3abaa..d48431a 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -700,28 +700,22 @@
      * @throws IOException if something goes wrong.
      */
     private static void bootCommand(Context context, String... args) throws IOException {
-        synchronized (sRequestLock) {
-            LOG_FILE.delete();
+        LOG_FILE.delete();
 
-            StringBuilder command = new StringBuilder();
-            for (String arg : args) {
-                if (!TextUtils.isEmpty(arg)) {
-                    command.append(arg);
-                    command.append("\n");
-                }
+        StringBuilder command = new StringBuilder();
+        for (String arg : args) {
+            if (!TextUtils.isEmpty(arg)) {
+                command.append(arg);
+                command.append("\n");
             }
-
-            // Write the command into BCB (bootloader control block).
-            RecoverySystem rs = (RecoverySystem) context.getSystemService(
-                    Context.RECOVERY_SERVICE);
-            rs.setupBcb(command.toString());
-
-            // Having set up the BCB, go ahead and reboot.
-            PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-            pm.reboot(PowerManager.REBOOT_RECOVERY);
-
-            throw new IOException("Reboot failed (no permissions?)");
         }
+
+        // Write the command into BCB (bootloader control block) and boot from
+        // there. Will not return unless failed.
+        RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
+        rs.rebootRecoveryWithCommand(command.toString());
+
+        throw new IOException("Reboot failed (no permissions?)");
     }
 
     // Read last_install; then report time (in seconds) and I/O (in MiB) for
@@ -916,6 +910,17 @@
     }
 
     /**
+     * Talks to RecoverySystemService via Binder to set up the BCB command and
+     * reboot into recovery accordingly.
+     */
+    private void rebootRecoveryWithCommand(String command) {
+        try {
+            mService.rebootRecoveryWithCommand(command);
+        } catch (RemoteException ignored) {
+        }
+    }
+
+    /**
      * Internally, recovery treats each line of the command file as a separate
      * argv, so we only need to protect against newlines and nulls.
      */
diff --git a/core/java/android/os/ShellCallback.java b/core/java/android/os/ShellCallback.java
new file mode 100644
index 0000000..e7fe697
--- /dev/null
+++ b/core/java/android/os/ShellCallback.java
@@ -0,0 +1,114 @@
+/*
+ * 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 android.os;
+
+import android.util.Log;
+
+import com.android.internal.os.IShellCallback;
+
+/**
+ * Special-purpose API for use with {@link IBinder#shellCommand IBinder.shellCommand} for
+ * performing operations back on the invoking shell.
+ * @hide
+ */
+public class ShellCallback implements Parcelable {
+    final static String TAG = "ShellCallback";
+
+    final static boolean DEBUG = false;
+
+    final boolean mLocal;
+
+    IShellCallback mShellCallback;
+
+    class MyShellCallback extends IShellCallback.Stub {
+        public ParcelFileDescriptor openOutputFile(String path, String seLinuxContext) {
+            return onOpenOutputFile(path, seLinuxContext);
+        }
+    }
+
+    /**
+     * Create a new ShellCallback to receive requests.
+     */
+    public ShellCallback() {
+        mLocal = true;
+    }
+
+    /**
+     * Ask the shell to open a file for writing.  This will truncate the file if it
+     * already exists.  It will create the file if it doesn't exist.
+     * @param path Path of the file to be opened/created.
+     * @param seLinuxContext Optional SELinux context that must be allowed to have
+     * access to the file; if null, nothing is required.
+     */
+    public ParcelFileDescriptor openOutputFile(String path, String seLinuxContext) {
+        if (DEBUG) Log.d(TAG, "openOutputFile " + this + ": mLocal=" + mLocal
+                + " mShellCallback=" + mShellCallback);
+
+        if (mLocal) {
+            return onOpenOutputFile(path, seLinuxContext);
+        }
+
+        if (mShellCallback != null) {
+            try {
+                return mShellCallback.openOutputFile(path, seLinuxContext);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failure opening " + path, e);
+            }
+        }
+        return null;
+    }
+
+    public ParcelFileDescriptor onOpenOutputFile(String path, String seLinuxContext) {
+        return null;
+    }
+
+    public static void writeToParcel(ShellCallback callback, Parcel out) {
+        if (callback == null) {
+            out.writeStrongBinder(null);
+        } else {
+            callback.writeToParcel(out, 0);
+        }
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        synchronized (this) {
+            if (mShellCallback == null) {
+                mShellCallback = new MyShellCallback();
+            }
+            out.writeStrongBinder(mShellCallback.asBinder());
+        }
+    }
+
+    ShellCallback(Parcel in) {
+        mLocal = false;
+        mShellCallback = IShellCallback.Stub.asInterface(in.readStrongBinder());
+    }
+
+    public static final Parcelable.Creator<ShellCallback> CREATOR
+            = new Parcelable.Creator<ShellCallback>() {
+        public ShellCallback createFromParcel(Parcel in) {
+            return new ShellCallback(in);
+        }
+        public ShellCallback[] newArray(int size) {
+            return new ShellCallback[size];
+        }
+    };
+}
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index fc804e5..831c9b2 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -40,6 +40,7 @@
     private FileDescriptor mOut;
     private FileDescriptor mErr;
     private String[] mArgs;
+    private ShellCallback mShellCallback;
     private ResultReceiver mResultReceiver;
 
     private String mCmd;
@@ -55,12 +56,13 @@
     private InputStream mInputStream;
 
     public void init(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
-            String[] args, int firstArgPos) {
+            String[] args, ShellCallback callback, int firstArgPos) {
         mTarget = target;
         mIn = in;
         mOut = out;
         mErr = err;
         mArgs = args;
+        mShellCallback = callback;
         mResultReceiver = null;
         mCmd = null;
         mArgPos = firstArgPos;
@@ -74,7 +76,7 @@
     }
 
     public int exec(Binder target, FileDescriptor in, FileDescriptor out, FileDescriptor err,
-            String[] args, ResultReceiver resultReceiver) {
+            String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
         String cmd;
         int start;
         if (args != null && args.length > 0) {
@@ -84,7 +86,7 @@
             cmd = null;
             start = 0;
         }
-        init(target, in, out, err, args, start);
+        init(target, in, out, err, args, callback, start);
         mCmd = cmd;
         mResultReceiver = resultReceiver;
 
@@ -105,7 +107,7 @@
             // go.
             PrintWriter eout = getErrPrintWriter();
             eout.println();
-            eout.println("Exception occurred while dumping:");
+            eout.println("Exception occurred while executing:");
             e.printStackTrace(eout);
         } finally {
             if (DEBUG) Slog.d(TAG, "Flushing output streams on " + mTarget);
@@ -257,6 +259,13 @@
         return arg;
     }
 
+    /**
+     * Return the {@link ShellCallback} for communicating back with the calling shell.
+     */
+    public ShellCallback getShellCallback() {
+        return mShellCallback;
+    }
+
     public int handleDefaultCommands(String cmd) {
         if ("dump".equals(cmd)) {
             String[] newArgs = new String[mArgs.length-1];
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index 672df6d..b3d76d7 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -20,6 +20,8 @@
 import android.content.Context;
 import android.util.Slog;
 
+import dalvik.annotation.optimization.CriticalNative;
+
 /**
  * Core timekeeping facilities.
  *
@@ -124,7 +126,7 @@
             }
             duration = start + ms - uptimeMillis();
         } while (duration > 0);
-        
+
         if (interrupted) {
             // Important: we don't want to quietly eat an interrupt() event,
             // so we make sure to re-interrupt the thread so that the next
@@ -132,7 +134,7 @@
             Thread.currentThread().interrupt();
         }
     }
-    
+
     /**
      * Sets the current wall time, in milliseconds.  Requires the calling
      * process to have appropriate permissions.
@@ -162,6 +164,7 @@
      *
      * @return milliseconds of non-sleep uptime since boot.
      */
+    @CriticalNative
     native public static long uptimeMillis();
 
     /**
@@ -169,6 +172,7 @@
      *
      * @return elapsed milliseconds since boot.
      */
+    @CriticalNative
     native public static long elapsedRealtime();
 
     /**
@@ -176,30 +180,34 @@
      *
      * @return elapsed nanoseconds since boot.
      */
+    @CriticalNative
     public static native long elapsedRealtimeNanos();
 
     /**
      * Returns milliseconds running in the current thread.
-     * 
+     *
      * @return elapsed milliseconds in the thread
      */
+    @CriticalNative
     public static native long currentThreadTimeMillis();
 
     /**
      * Returns microseconds running in the current thread.
-     * 
+     *
      * @return elapsed microseconds in the thread
-     * 
+     *
      * @hide
      */
+    @CriticalNative
     public static native long currentThreadTimeMicro();
 
     /**
      * Returns current wall time in  microseconds.
-     * 
+     *
      * @return elapsed microseconds in wall time
-     * 
+     *
      * @hide
      */
+    @CriticalNative
     public static native long currentTimeMicro();
 }
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 3ae28fd..7e8cc0b8 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -16,6 +16,8 @@
 
 package android.os;
 
+import dalvik.annotation.optimization.FastNative;
+
 /**
  * Writes trace events to the system trace buffer.  These trace events can be
  * collected and visualized using the Systrace tool.
@@ -91,14 +93,20 @@
     private static volatile long sEnabledTags = TRACE_TAG_NOT_READY;
 
     private static native long nativeGetEnabledTags();
-    private static native void nativeTraceCounter(long tag, String name, int value);
-    private static native void nativeTraceBegin(long tag, String name);
-    private static native void nativeTraceEnd(long tag);
-    private static native void nativeAsyncTraceBegin(long tag, String name, int cookie);
-    private static native void nativeAsyncTraceEnd(long tag, String name, int cookie);
     private static native void nativeSetAppTracingAllowed(boolean allowed);
     private static native void nativeSetTracingEnabled(boolean allowed);
 
+    @FastNative
+    private static native void nativeTraceCounter(long tag, String name, int value);
+    @FastNative
+    private static native void nativeTraceBegin(long tag, String name);
+    @FastNative
+    private static native void nativeTraceEnd(long tag);
+    @FastNative
+    private static native void nativeAsyncTraceBegin(long tag, String name, int cookie);
+    @FastNative
+    private static native void nativeAsyncTraceEnd(long tag, String name, int cookie);
+
     static {
         // We configure two separate change callbacks, one in Trace.cpp and one here.  The
         // native callback reads the tags from the system property, and this callback
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index f134943..3d094f7 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -586,8 +586,6 @@
      *
      * @param key The option key.
      * @return Whether the option is present.
-     *
-     * @hide
      */
     public boolean hasAdvancedOption(String key) {
         return mAdvancedOptions != null && mAdvancedOptions.containsKey(key);
@@ -598,8 +596,6 @@
      *
      * @param key The option key.
      * @return The option value.
-     *
-     * @hide
      */
     public String getAdvancedStringOption(String key) {
         if (mAdvancedOptions != null) {
@@ -613,8 +609,6 @@
      *
      * @param key The option key.
      * @return The option value.
-     *
-     * @hide
      */
     public int getAdvancedIntOption(String key) {
         if (mAdvancedOptions != null) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index f8b9361..bf73089 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8968,6 +8968,14 @@
         public static final String DATABASE_DOWNGRADE_REASON = "database_downgrade_reason";
 
         /**
+         * Flag to toggle journal mode WAL on or off for the contacts database. WAL is enabled by
+         * default. Set to 0 to disable.
+         *
+         * @hide
+         */
+        public static final String CONTACTS_DATABASE_WAL_ENABLED = "contacts_database_wal_enabled";
+
+        /**
          * Settings to backup. This is here so that it's in the same place as the settings
          * keys and easy to update.
          *
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 5a9a1ea..4393992 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import dalvik.annotation.optimization.FastNative;
 import dalvik.system.CloseGuard;
 
 import android.os.Looper;
@@ -47,6 +48,7 @@
     private static native long nativeInit(WeakReference<DisplayEventReceiver> receiver,
             MessageQueue messageQueue);
     private static native void nativeDispose(long receiverPtr);
+    @FastNative
     private static native void nativeScheduleVsync(long receiverPtr);
 
     /**
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index fab5364..3e8d577 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -23,6 +23,9 @@
 import android.os.SystemClock;
 import android.util.SparseArray;
 
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
 /**
  * Object used to report movement (mouse, pen, finger, trackball) events.
  * Motion events may hold either absolute or relative movements and other data,
@@ -1445,60 +1448,98 @@
             float xOffset, float yOffset, float xPrecision, float yPrecision,
             long downTimeNanos, long eventTimeNanos,
             int pointerCount, PointerProperties[] pointerIds, PointerCoords[] pointerCoords);
-    private static native long nativeCopy(long destNativePtr, long sourceNativePtr,
-            boolean keepHistory);
     private static native void nativeDispose(long nativePtr);
     private static native void nativeAddBatch(long nativePtr, long eventTimeNanos,
             PointerCoords[] pointerCoords, int metaState);
-
-    private static native int nativeGetDeviceId(long nativePtr);
-    private static native int nativeGetSource(long nativePtr);
-    private static native int nativeSetSource(long nativePtr, int source);
-    private static native int nativeGetAction(long nativePtr);
-    private static native void nativeSetAction(long nativePtr, int action);
-    private static native boolean nativeIsTouchEvent(long nativePtr);
-    private static native int nativeGetFlags(long nativePtr);
-    private static native void nativeSetFlags(long nativePtr, int flags);
-    private static native int nativeGetEdgeFlags(long nativePtr);
-    private static native void nativeSetEdgeFlags(long nativePtr, int action);
-    private static native int nativeGetMetaState(long nativePtr);
-    private static native int nativeGetButtonState(long nativePtr);
-    private static native void nativeSetButtonState(long nativePtr, int buttonState);
-    private static native int nativeGetActionButton(long nativePtr);
-    private static native void nativeSetActionButton(long nativePtr, int actionButton);
-    private static native void nativeOffsetLocation(long nativePtr, float deltaX, float deltaY);
-    private static native float nativeGetXOffset(long nativePtr);
-    private static native float nativeGetYOffset(long nativePtr);
-    private static native float nativeGetXPrecision(long nativePtr);
-    private static native float nativeGetYPrecision(long nativePtr);
-    private static native long nativeGetDownTimeNanos(long nativePtr);
-    private static native void nativeSetDownTimeNanos(long nativePtr, long downTime);
-
-    private static native int nativeGetPointerCount(long nativePtr);
-    private static native int nativeGetPointerId(long nativePtr, int pointerIndex);
-    private static native int nativeGetToolType(long nativePtr, int pointerIndex);
-    private static native int nativeFindPointerIndex(long nativePtr, int pointerId);
-
-    private static native int nativeGetHistorySize(long nativePtr);
-    private static native long nativeGetEventTimeNanos(long nativePtr, int historyPos);
-    private static native float nativeGetRawAxisValue(long nativePtr,
-            int axis, int pointerIndex, int historyPos);
-    private static native float nativeGetAxisValue(long nativePtr,
-            int axis, int pointerIndex, int historyPos);
     private static native void nativeGetPointerCoords(long nativePtr,
             int pointerIndex, int historyPos, PointerCoords outPointerCoords);
     private static native void nativeGetPointerProperties(long nativePtr,
             int pointerIndex, PointerProperties outPointerProperties);
 
-    private static native void nativeScale(long nativePtr, float scale);
-    private static native void nativeTransform(long nativePtr, Matrix matrix);
-
     private static native long nativeReadFromParcel(long nativePtr, Parcel parcel);
     private static native void nativeWriteToParcel(long nativePtr, Parcel parcel);
 
     private static native String nativeAxisToString(int axis);
     private static native int nativeAxisFromString(String label);
 
+    // -------------- @FastNative -------------------------
+
+    @FastNative
+    private static native int nativeGetPointerId(long nativePtr, int pointerIndex);
+    @FastNative
+    private static native int nativeGetToolType(long nativePtr, int pointerIndex);
+    @FastNative
+    private static native long nativeGetEventTimeNanos(long nativePtr, int historyPos);
+    @FastNative
+    private static native float nativeGetRawAxisValue(long nativePtr,
+            int axis, int pointerIndex, int historyPos);
+    @FastNative
+    private static native float nativeGetAxisValue(long nativePtr,
+            int axis, int pointerIndex, int historyPos);
+
+    // -------------- @CriticalNative ----------------------
+
+    @CriticalNative
+    private static native long nativeCopy(long destNativePtr, long sourceNativePtr,
+            boolean keepHistory);
+    @CriticalNative
+    private static native int nativeGetDeviceId(long nativePtr);
+    @CriticalNative
+    private static native int nativeGetSource(long nativePtr);
+    @CriticalNative
+    private static native int nativeSetSource(long nativePtr, int source);
+    @CriticalNative
+    private static native int nativeGetAction(long nativePtr);
+    @CriticalNative
+    private static native void nativeSetAction(long nativePtr, int action);
+    @CriticalNative
+    private static native boolean nativeIsTouchEvent(long nativePtr);
+    @CriticalNative
+    private static native int nativeGetFlags(long nativePtr);
+    @CriticalNative
+    private static native void nativeSetFlags(long nativePtr, int flags);
+    @CriticalNative
+    private static native int nativeGetEdgeFlags(long nativePtr);
+    @CriticalNative
+    private static native void nativeSetEdgeFlags(long nativePtr, int action);
+    @CriticalNative
+    private static native int nativeGetMetaState(long nativePtr);
+    @CriticalNative
+    private static native int nativeGetButtonState(long nativePtr);
+    @CriticalNative
+    private static native void nativeSetButtonState(long nativePtr, int buttonState);
+    @CriticalNative
+    private static native int nativeGetActionButton(long nativePtr);
+    @CriticalNative
+    private static native void nativeSetActionButton(long nativePtr, int actionButton);
+    @CriticalNative
+    private static native void nativeOffsetLocation(long nativePtr, float deltaX, float deltaY);
+    @CriticalNative
+    private static native float nativeGetXOffset(long nativePtr);
+    @CriticalNative
+    private static native float nativeGetYOffset(long nativePtr);
+    @CriticalNative
+    private static native float nativeGetXPrecision(long nativePtr);
+    @CriticalNative
+    private static native float nativeGetYPrecision(long nativePtr);
+    @CriticalNative
+    private static native long nativeGetDownTimeNanos(long nativePtr);
+    @CriticalNative
+    private static native void nativeSetDownTimeNanos(long nativePtr, long downTime);
+
+    @CriticalNative
+    private static native int nativeGetPointerCount(long nativePtr);
+    @CriticalNative
+    private static native int nativeFindPointerIndex(long nativePtr, int pointerId);
+
+    @CriticalNative
+    private static native int nativeGetHistorySize(long nativePtr);
+
+    @CriticalNative
+    private static native void nativeScale(long nativePtr, float scale);
+    @CriticalNative
+    private static native void nativeTransform(long nativePtr, long matrix);
+
     private MotionEvent() {
     }
 
@@ -2065,7 +2106,7 @@
     public final int getPointerCount() {
         return nativeGetPointerCount(mNativePtr);
     }
-    
+
     /**
      * Return the pointer identifier associated with a particular pointer
      * data index in this event.  The identifier tells you the actual pointer
@@ -2891,7 +2932,7 @@
             throw new IllegalArgumentException("matrix must not be null");
         }
 
-        nativeTransform(mNativePtr, matrix);
+        nativeTransform(mNativePtr, matrix.native_instance);
     }
 
     /**
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 2a2f659..816bcf0 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -600,7 +600,9 @@
                             // surfaceDestroyed and surfaceCreated, we force a disconnect,
                             // so the next connect will always work if we end up reusing
                             // the surface.
-                            mSurface.forceScopedDisconnect();
+                            if (mSurface.isValid()) {
+                                mSurface.forceScopedDisconnect();
+                            }
                         }
                     }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d060aac..deb3f90 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -20661,7 +20661,9 @@
             return false;
         }
 
-        data.prepareToLeaveProcess((flags & View.DRAG_FLAG_GLOBAL) != 0);
+        if (data != null) {
+            data.prepareToLeaveProcess((flags & View.DRAG_FLAG_GLOBAL) != 0);
+        }
 
         boolean okay = false;
 
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 1b3f6ff..2e428a7 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -20,6 +20,7 @@
 import android.annotation.IdRes;
 import android.annotation.NonNull;
 import android.annotation.UiThread;
+import android.content.ClipData;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -1380,6 +1381,7 @@
         boolean retval = false;
         final float tx = event.mX;
         final float ty = event.mY;
+        final ClipData td = event.mClipData;
 
         // Dispatch down the view hierarchy
         final PointF localPoint = getLocalPoint();
@@ -1465,6 +1467,7 @@
                     // ACTION_DRAG_EXITED.
                     event.mX = 0;
                     event.mY = 0;
+                    event.mClipData = null;
 
                     if (mCurrentDragChild != null) {
                         event.mAction = DragEvent.ACTION_DRAG_EXITED;
@@ -1479,6 +1482,7 @@
                     event.mAction = action;
                     event.mX = tx;
                     event.mY = ty;
+                    event.mClipData = td;
                 }
                 mCurrentDragChild = target;
             }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index ec29abf..87b330d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -29,6 +29,7 @@
 import android.annotation.NonNull;
 import android.app.ActivityManagerNative;
 import android.app.ResourcesManager;
+import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.ComponentCallbacks;
 import android.content.Context;
@@ -5551,8 +5552,8 @@
                 // Remember who the current drag target is pre-dispatch
                 final View prevDragView = mCurrentDragView;
 
-                if (what == DragEvent.ACTION_DROP) {
-                    event.getClipData().prepareToEnterProcess();
+                if (what == DragEvent.ACTION_DROP && event.mClipData != null) {
+                    event.mClipData.prepareToEnterProcess();
                 }
 
                 // Now dispatch the drag/drop event
@@ -5657,9 +5658,11 @@
             final float tx = event.mX;
             final float ty = event.mY;
             final int action = event.mAction;
+            final ClipData td = event.mClipData;
             // Position should not be available for ACTION_DRAG_ENTERED and ACTION_DRAG_EXITED.
             event.mX = 0;
             event.mY = 0;
+            event.mClipData = null;
 
             if (mCurrentDragView != null) {
                 event.mAction = DragEvent.ACTION_DRAG_EXITED;
@@ -5674,6 +5677,7 @@
             event.mAction = action;
             event.mX = tx;
             event.mY = ty;
+            event.mClipData = td;
         }
 
         mCurrentDragView = newDragTarget;
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index aaa7b26..8c725cf 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1768,6 +1768,7 @@
      * requests. This will replace the current handler.
      *
      * @param client an implementation of WebViewClient
+     * @see #getWebViewClient
      */
     public void setWebViewClient(WebViewClient client) {
         checkThread();
@@ -1775,6 +1776,17 @@
     }
 
     /**
+     * Gets the WebViewClient.
+     *
+     * @return the WebViewClient, or a default client if not yet set
+     * @see #setWebViewClient
+     */
+    public WebViewClient getWebViewClient() {
+        checkThread();
+        return mProvider.getWebViewClient();
+    }
+
+    /**
      * Registers the interface to be used when content can not be handled by
      * the rendering engine, and should be downloaded instead. This will replace
      * the current handler.
@@ -1792,6 +1804,7 @@
      * This will replace the current handler.
      *
      * @param client an implementation of WebChromeClient
+     * @see #getWebChromeClient
      */
     public void setWebChromeClient(WebChromeClient client) {
         checkThread();
@@ -1799,6 +1812,17 @@
     }
 
     /**
+     * Gets the chrome handler.
+     *
+     * @return the WebChromeClient, or null if not yet set
+     * @see #setWebChromeClient
+     */
+    public WebChromeClient getWebChromeClient() {
+        checkThread();
+        return mProvider.getWebChromeClient();
+    }
+
+    /**
      * Sets the Picture listener. This is an interface used to receive
      * notifications of a new Picture.
      *
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index e5b65e7..95ec179 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -229,10 +229,14 @@
 
     public void setWebViewClient(WebViewClient client);
 
+    public WebViewClient getWebViewClient();
+
     public void setDownloadListener(DownloadListener listener);
 
     public void setWebChromeClient(WebChromeClient client);
 
+    public WebChromeClient getWebChromeClient();
+
     public void setPictureListener(PictureListener listener);
 
     public void addJavascriptInterface(Object obj, String interfaceName);
diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java
index c067da7..3baccee 100644
--- a/core/java/com/android/internal/os/BaseCommand.java
+++ b/core/java/com/android/internal/os/BaseCommand.java
@@ -36,6 +36,8 @@
     public static final String NO_SYSTEM_ERROR_CODE = "Error type 2";
     public static final String NO_CLASS_ERROR_CODE = "Error type 3";
 
+    private String[] mRawArgs;
+
     /**
      * Call to run the command.
      */
@@ -45,7 +47,8 @@
             return;
         }
 
-        mArgs.init(null, null, null, null, args, 0);
+        mRawArgs = args;
+        mArgs.init(null, null, null, null, args, null, 0);
 
         try {
             onRun();
@@ -109,4 +112,11 @@
     public String nextArgRequired() {
         return mArgs.getNextArgRequired();
     }
+
+    /**
+     * Return the original raw argument list supplied to the command.
+     */
+    public String[] getRawArgs() {
+        return mRawArgs;
+    }
 }
diff --git a/core/java/com/android/internal/os/IShellCallback.aidl b/core/java/com/android/internal/os/IShellCallback.aidl
new file mode 100644
index 0000000..57d6789
--- /dev/null
+++ b/core/java/com/android/internal/os/IShellCallback.aidl
@@ -0,0 +1,24 @@
+/**
+ * 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.internal.os;
+
+import android.os.ParcelFileDescriptor;
+
+/** @hide */
+interface IShellCallback {
+    ParcelFileDescriptor openOutputFile(String path, String seLinuxContext);
+}
diff --git a/core/java/com/android/internal/policy/BackdropFrameRenderer.java b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
index 619303f..1abb59b 100644
--- a/core/java/com/android/internal/policy/BackdropFrameRenderer.java
+++ b/core/java/com/android/internal/policy/BackdropFrameRenderer.java
@@ -110,12 +110,15 @@
             int statusBarColor, int navigationBarColor) {
         mDecorView = decorView;
         mResizingBackgroundDrawable = resizingBackgroundDrawable != null
+                        && resizingBackgroundDrawable.getConstantState() != null
                 ? resizingBackgroundDrawable.getConstantState().newDrawable()
                 : null;
         mCaptionBackgroundDrawable = captionBackgroundDrawableDrawable != null
+                        && captionBackgroundDrawableDrawable.getConstantState() != null
                 ? captionBackgroundDrawableDrawable.getConstantState().newDrawable()
                 : null;
         mUserCaptionBackgroundDrawable = userCaptionBackgroundDrawable != null
+                        && userCaptionBackgroundDrawable.getConstantState() != null
                 ? userCaptionBackgroundDrawable.getConstantState().newDrawable()
                 : null;
         if (mCaptionBackgroundDrawable == null) {
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 18c4ee3..6acb76d 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -127,12 +127,10 @@
     }
 };
 
-Bitmap::Bitmap(JNIEnv* env, jbyteArray storageObj, void* address,
-            const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
-        : mPixelStorageType(PixelStorageType::Java) {
-    env->GetJavaVM(&mPixelStorage.java.jvm);
-    mPixelStorage.java.jweakRef = env->NewWeakGlobalRef(storageObj);
-    mPixelStorage.java.jstrongRef = nullptr;
+Bitmap::Bitmap(void* address, size_t size, const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable)
+        : mPixelStorageType(PixelStorageType::Heap) {
+    mPixelStorage.heap.address = address;
+    mPixelStorage.heap.size = size;
     mPixelRef.reset(new WrappedPixelRef(this, address, info, rowBytes, ctable));
     // Note: this will trigger a call to onStrongRefDestroyed(), but
     // we want the pixel ref to have a ref count of 0 at this point
@@ -187,12 +185,8 @@
         munmap(mPixelStorage.ashmem.address, mPixelStorage.ashmem.size);
         close(mPixelStorage.ashmem.fd);
         break;
-    case PixelStorageType::Java:
-        JNIEnv* env = jniEnv();
-        LOG_ALWAYS_FATAL_IF(mPixelStorage.java.jstrongRef,
-                "Deleting a bitmap wrapper while there are outstanding strong "
-                "references! mPinnedRefCount = %d", mPinnedRefCount);
-        env->DeleteWeakGlobalRef(mPixelStorage.java.jweakRef);
+    case PixelStorageType::Heap:
+        free(mPixelStorage.heap.address);
         break;
     }
 
@@ -219,6 +213,15 @@
     }
 }
 
+size_t Bitmap::getAllocationByteCount() const {
+    switch (mPixelStorageType) {
+    case PixelStorageType::Heap:
+        return mPixelStorage.heap.size;
+    default:
+        return rowBytes() * height();
+    }
+}
+
 const SkImageInfo& Bitmap::info() const {
     return mPixelRef->info();
 }
@@ -244,7 +247,6 @@
         // We just restored this from 0, pin the pixels and inc the strong count
         // Note that there *might be* an incoming onStrongRefDestroyed from whatever
         // last unref'd
-        pinPixelsLocked();
         mPinnedRefCount++;
     }
     return mPixelRef.get();
@@ -283,13 +285,6 @@
     return mPinnedRefCount == 0 && !mAttachedToJava;
 }
 
-JNIEnv* Bitmap::jniEnv() {
-    JNIEnv* env;
-    auto success = mPixelStorage.java.jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
-    LOG_ALWAYS_FATAL_IF(success != JNI_OK,
-        "Failed to get JNIEnv* from JVM: %p", mPixelStorage.java.jvm);
-    return env;
-}
 
 void Bitmap::onStrongRefDestroyed() {
     bool disposeSelf = false;
@@ -298,7 +293,6 @@
         if (mPinnedRefCount > 0) {
             mPinnedRefCount--;
             if (mPinnedRefCount == 0) {
-                unpinPixelsLocked();
                 disposeSelf = shouldDisposeSelfLocked();
             }
         }
@@ -308,49 +302,6 @@
     }
 }
 
-void Bitmap::pinPixelsLocked() {
-    switch (mPixelStorageType) {
-    case PixelStorageType::Invalid:
-        LOG_ALWAYS_FATAL("Cannot pin invalid pixels!");
-        break;
-    case PixelStorageType::External:
-    case PixelStorageType::Ashmem:
-        // Nothing to do
-        break;
-    case PixelStorageType::Java: {
-        JNIEnv* env = jniEnv();
-        if (!mPixelStorage.java.jstrongRef) {
-            mPixelStorage.java.jstrongRef = reinterpret_cast<jbyteArray>(
-                    env->NewGlobalRef(mPixelStorage.java.jweakRef));
-            if (!mPixelStorage.java.jstrongRef) {
-                LOG_ALWAYS_FATAL("Failed to acquire strong reference to pixels");
-            }
-        }
-        break;
-    }
-    }
-}
-
-void Bitmap::unpinPixelsLocked() {
-    switch (mPixelStorageType) {
-    case PixelStorageType::Invalid:
-        LOG_ALWAYS_FATAL("Cannot unpin invalid pixels!");
-        break;
-    case PixelStorageType::External:
-    case PixelStorageType::Ashmem:
-        // Don't need to do anything
-        break;
-    case PixelStorageType::Java: {
-        JNIEnv* env = jniEnv();
-        if (mPixelStorage.java.jstrongRef) {
-            env->DeleteGlobalRef(mPixelStorage.java.jstrongRef);
-            mPixelStorage.java.jstrongRef = nullptr;
-        }
-        break;
-    }
-    }
-}
-
 void Bitmap::getSkBitmap(SkBitmap* outBitmap) {
     assertValid();
     android::AutoMutex _lock(mLock);
@@ -723,7 +674,7 @@
     SkBitmap bitmap;
     bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType));
 
-    Bitmap* nativeBitmap = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);
+    Bitmap* nativeBitmap = GraphicsJNI::allocateHeapPixelRef(&bitmap, NULL);
     if (!nativeBitmap) {
         return NULL;
     }
@@ -742,8 +693,8 @@
     SkBitmap src;
     reinterpret_cast<Bitmap*>(srcHandle)->getSkBitmap(&src);
     SkColorType dstCT = GraphicsJNI::legacyBitmapConfigToColorType(dstConfigHandle);
-    SkBitmap            result;
-    JavaPixelAllocator  allocator(env);
+    SkBitmap result;
+    HeapAllocator allocator;
 
     if (!src.copyTo(&result, dstCT, &allocator)) {
         return NULL;
@@ -798,8 +749,7 @@
 }
 
 static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jlong bitmapHandle,
-        jint width, jint height, jint configHandle, jint allocSize,
-        jboolean requestPremul) {
+        jint width, jint height, jint configHandle, jboolean requestPremul) {
     LocalScopedBitmap bitmap(bitmapHandle);
     SkColorType colorType = GraphicsJNI::legacyBitmapConfigToColorType(configHandle);
 
@@ -807,8 +757,8 @@
     if (colorType == kARGB_4444_SkColorType) {
         colorType = kN32_SkColorType;
     }
-
-    if (width * height * SkColorTypeBytesPerPixel(colorType) > allocSize) {
+    size_t requestedSize = width * height * SkColorTypeBytesPerPixel(colorType);
+    if (requestedSize > bitmap->getAllocationByteCount()) {
         // done in native as there's no way to get BytesPerPixel in Java
         doThrowIAE(env, "Bitmap not large enough to support new configuration");
         return;
@@ -1053,7 +1003,7 @@
 #endif
 
         // Copy the pixels into a new buffer.
-        nativeBitmap = GraphicsJNI::allocateJavaPixelRef(env, bitmap.get(), ctable);
+        nativeBitmap = GraphicsJNI::allocateHeapPixelRef(bitmap.get(), ctable);
         SkSafeUnref(ctable);
         if (!nativeBitmap) {
             blob.release();
@@ -1165,7 +1115,7 @@
     const android::Paint* paint = reinterpret_cast<android::Paint*>(paintHandle);
     SkIPoint  offset;
     SkBitmap dst;
-    JavaPixelAllocator allocator(env);
+    HeapAllocator allocator;
 
     src.extractAlpha(&dst, paint, &allocator, &offset);
     // If Skia can't allocate pixels for destination bitmap, it resets
@@ -1370,6 +1320,11 @@
     android::uirenderer::renderthread::RenderProxy::prepareToDraw(bitmap);
 }
 
+static jint Bitmap_getAllocationByteCount(JNIEnv* env, jobject, jlong bitmapPtr) {
+    LocalScopedBitmap bitmapHandle(bitmapPtr);
+    return static_cast<jint>(bitmapHandle->getAllocationByteCount());
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static const JNINativeMethod gBitmapMethods[] = {
@@ -1383,7 +1338,7 @@
         (void*)Bitmap_copyAshmemConfig },
     {   "nativeGetNativeFinalizer", "()J", (void*)Bitmap_getNativeFinalizer },
     {   "nativeRecycle",            "(J)Z", (void*)Bitmap_recycle },
-    {   "nativeReconfigure",        "(JIIIIZ)V", (void*)Bitmap_reconfigure },
+    {   "nativeReconfigure",        "(JIIIZ)V", (void*)Bitmap_reconfigure },
     {   "nativeCompress",           "(JIILjava/io/OutputStream;[B)Z",
         (void*)Bitmap_compress },
     {   "nativeErase",              "(JI)V", (void*)Bitmap_erase },
@@ -1414,6 +1369,7 @@
     {   "nativeSameAs",             "(JJ)Z", (void*)Bitmap_sameAs },
     {   "nativeRefPixelRef",        "(J)J", (void*)Bitmap_refPixelRef },
     {   "nativePrepareToDraw",      "(J)V", (void*)Bitmap_prepareToDraw },
+    {   "nativeGetAllocationByteCount", "(J)I", (void*)Bitmap_getAllocationByteCount },
 };
 
 int register_android_graphics_Bitmap(JNIEnv* env)
diff --git a/core/jni/android/graphics/Bitmap.h b/core/jni/android/graphics/Bitmap.h
index aaea178..9ae1f3f 100644
--- a/core/jni/android/graphics/Bitmap.h
+++ b/core/jni/android/graphics/Bitmap.h
@@ -28,7 +28,7 @@
 enum class PixelStorageType {
     Invalid,
     External,
-    Java,
+    Heap,
     Ashmem,
 };
 
@@ -47,8 +47,8 @@
  */
 class Bitmap {
 public:
-    Bitmap(JNIEnv* env, jbyteArray storageObj, void* address,
-            const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
+    Bitmap(void* address, size_t allocSize, const SkImageInfo& info, size_t rowBytes,
+            SkColorTable* ctable);
     Bitmap(void* address, void* context, FreeFunc freeFunc,
             const SkImageInfo& info, size_t rowBytes, SkColorTable* ctable);
     Bitmap(void* address, int fd, size_t mappedSize, const SkImageInfo& info,
@@ -56,12 +56,6 @@
 
     const SkImageInfo& info() const;
 
-    // Returns nullptr if it is not backed by a jbyteArray
-    jbyteArray javaByteArray() const {
-        return mPixelStorageType == PixelStorageType::Java
-                ? mPixelStorage.java.jstrongRef : nullptr;
-    }
-
     int width() const { return info().width(); }
     int height() const { return info().height(); }
     size_t rowBytes() const;
@@ -81,6 +75,7 @@
     bool hasHardwareMipMap();
     void setHasHardwareMipMap(bool hasMipMap);
     int getAshmemFd() const;
+    size_t getAllocationByteCount() const;
 
 private:
     friend class WrappedPixelRef;
@@ -90,8 +85,6 @@
     void onStrongRefDestroyed();
 
     void pinPixelsLocked();
-    void unpinPixelsLocked();
-    JNIEnv* jniEnv();
     bool shouldDisposeSelfLocked();
     void assertValid() const;
     SkPixelRef* refPixelRefLocked();
@@ -114,10 +107,9 @@
             size_t size;
         } ashmem;
         struct {
-            JavaVM* jvm;
-            jweak jweakRef;
-            jbyteArray jstrongRef;
-        } java;
+            void* address;
+            size_t size;
+        } heap;
     } mPixelStorage;
 };
 
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 77799d6..5a540ce 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -193,7 +193,7 @@
         bitmap->setPixelRef(mBitmap->refPixelRef())->unref();
 
         // since we're already allocated, we lockPixels right away
-        // HeapAllocator/JavaPixelAllocator behaves this way too
+        // HeapAllocator behaves this way too
         bitmap->lockPixels();
         return true;
     }
@@ -339,7 +339,7 @@
         }
     }
 
-    JavaPixelAllocator javaAllocator(env);
+    HeapAllocator defaultAllocator;
     RecyclingPixelAllocator recyclingAllocator(reuseBitmap, existingBufferSize);
     ScaleCheckingAllocator scaleCheckingAllocator(scale, existingBufferSize);
     SkBitmap::HeapAllocator heapAllocator;
@@ -353,10 +353,10 @@
         decodeAllocator = &recyclingAllocator;
     } else if (willScale) {
         // This will allocate pixels using a HeapAllocator, since there will be an extra
-        // scaling step that copies these pixels into Java memory.
+        // scaling step.
         decodeAllocator = &heapAllocator;
     } else {
-        decodeAllocator = &javaAllocator;
+        decodeAllocator = &defaultAllocator;
     }
 
     // Set the decode colorType.  This is necessary because we can't always support
@@ -412,7 +412,7 @@
 
     // Use SkAndroidCodec to perform the decode.
     SkAndroidCodec::AndroidOptions codecOptions;
-    codecOptions.fZeroInitialized = (decodeAllocator == &javaAllocator) ?
+    codecOptions.fZeroInitialized =  decodeAllocator == &defaultAllocator ?
             SkCodec::kYes_ZeroInitialized : SkCodec::kNo_ZeroInitialized;
     codecOptions.fColorPtr = colorPtr;
     codecOptions.fColorCount = colorCount;
@@ -477,7 +477,7 @@
         if (javaBitmap != nullptr) {
             outputAllocator = &recyclingAllocator;
         } else {
-            outputAllocator = &javaAllocator;
+            outputAllocator = &defaultAllocator;
         }
 
         SkColorType scaledColorType = colorTypeForScaledOutput(decodingBitmap.colorType());
@@ -540,7 +540,7 @@
     if (isPremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
 
     // now create the java bitmap
-    return GraphicsJNI::createBitmap(env, javaAllocator.getStorageObjAndReset(),
+    return GraphicsJNI::createBitmap(env, defaultAllocator.getStorageObjAndReset(),
             bitmapCreateFlags, ninePatchChunk, ninePatchInsets, -1);
 }
 
diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp
index 970001a..21850bd 100644
--- a/core/jni/android/graphics/BitmapRegionDecoder.cpp
+++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp
@@ -161,13 +161,13 @@
     // Set up the pixel allocator
     SkBRDAllocator* allocator = nullptr;
     RecyclingClippingPixelAllocator recycleAlloc(recycledBitmap, recycledBytes);
-    JavaPixelAllocator javaAlloc(env);
+    HeapAllocator heapAlloc;
     if (javaBitmap) {
         allocator = &recycleAlloc;
         // We are required to match the color type of the recycled bitmap.
         colorType = recycledBitmap->info().colorType();
     } else {
-        allocator = &javaAlloc;
+        allocator = &heapAlloc;
     }
 
     // Decode the region.
@@ -200,7 +200,7 @@
     if (!requireUnpremul) {
         bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
     }
-    return GraphicsJNI::createBitmap(env, javaAlloc.getStorageObjAndReset(), bitmapCreateFlags);
+    return GraphicsJNI::createBitmap(env, heapAlloc.getStorageObjAndReset(), bitmapCreateFlags);
 }
 
 static jint nativeGetHeight(JNIEnv* env, jobject, jlong brdHandle) {
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index c6a51e8..8cee814 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -416,9 +416,8 @@
     assert_premultiplied(bitmap->info(), isPremultiplied);
 
     jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
-            reinterpret_cast<jlong>(bitmap), bitmap->javaByteArray(),
-            bitmap->width(), bitmap->height(), density, isMutable, isPremultiplied,
-            ninePatchChunk, ninePatchInsets);
+            reinterpret_cast<jlong>(bitmap), bitmap->width(), bitmap->height(), density, isMutable,
+            isPremultiplied, ninePatchChunk, ninePatchInsets);
     hasException(env); // For the side effect of logging.
     return obj;
 }
@@ -483,37 +482,28 @@
     return true;
 }
 
-android::Bitmap* GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
-                                             SkColorTable* ctable) {
+android::Bitmap* GraphicsJNI::allocateHeapPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
     const SkImageInfo& info = bitmap->info();
     if (info.colorType() == kUnknown_SkColorType) {
-        doThrowIAE(env, "unknown bitmap configuration");
-        return NULL;
+        LOG_ALWAYS_FATAL("unknown bitmap configuration");
+        return nullptr;
     }
 
     size_t size;
     if (!computeAllocationSize(*bitmap, &size)) {
-        return NULL;
+        return nullptr;
     }
 
     // we must respect the rowBytes value already set on the bitmap instead of
     // attempting to compute our own.
     const size_t rowBytes = bitmap->rowBytes();
 
-    jbyteArray arrayObj = (jbyteArray) env->CallObjectMethod(gVMRuntime,
-                                                             gVMRuntime_newNonMovableArray,
-                                                             gByte_class, size);
-    if (env->ExceptionCheck() != 0) {
-        return NULL;
+    void* addr = calloc(size, 1);
+    if (!addr) {
+        return nullptr;
     }
-    SkASSERT(arrayObj);
-    jbyte* addr = (jbyte*) env->CallLongMethod(gVMRuntime, gVMRuntime_addressOf, arrayObj);
-    if (env->ExceptionCheck() != 0) {
-        return NULL;
-    }
-    SkASSERT(addr);
-    android::Bitmap* wrapper = new android::Bitmap(env, arrayObj, (void*) addr,
-            info, rowBytes, ctable);
+
+    android::Bitmap* wrapper = new android::Bitmap(addr, size, info, rowBytes, ctable);
     wrapper->getSkBitmap(bitmap);
     // since we're already allocated, we lockPixels right away
     // HeapAllocator behaves this way too
@@ -658,21 +648,16 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env) {
-    LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&mJavaVM) != JNI_OK,
-            "env->GetJavaVM failed");
-}
+HeapAllocator::HeapAllocator() {}
 
-JavaPixelAllocator::~JavaPixelAllocator() {
+HeapAllocator::~HeapAllocator() {
     if (mStorage) {
         mStorage->detachFromJava();
     }
 }
 
-bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
-    JNIEnv* env = vm2env(mJavaVM);
-
-    mStorage = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
+bool HeapAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
+    mStorage = GraphicsJNI::allocateHeapPixelRef(bitmap, ctable);
     return mStorage != nullptr;
 }
 
@@ -830,7 +815,7 @@
 
     gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
     gBitmap_nativePtr = getFieldIDCheck(env, gBitmap_class, "mNativePtr", "J");
-    gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(J[BIIIZZ[BLandroid/graphics/NinePatch$InsetStruct;)V");
+    gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(JIIIZZ[BLandroid/graphics/NinePatch$InsetStruct;)V");
     gBitmap_reinitMethodID = env->GetMethodID(gBitmap_class, "reinit", "(IIZ)V");
     gBitmap_getAllocationByteCountMethodID = env->GetMethodID(gBitmap_class, "getAllocationByteCount", "()I");
     gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 89636db..738ad54 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -93,8 +93,7 @@
 
     static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
 
-    static android::Bitmap* allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
-            SkColorTable* ctable);
+    static android::Bitmap* allocateHeapPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
 
     static android::Bitmap* allocateAshmemPixelRef(JNIEnv* env, SkBitmap* bitmap,
             SkColorTable* ctable);
@@ -119,15 +118,10 @@
             const SkBitmap& dstBitmap);
 };
 
-/** Allocator which allocates the backing buffer in the Java heap.
- *  Instances can only be used to perform a single allocation, which helps
- *  ensure that the allocated buffer is properly accounted for with a
- *  reference in the heap (or a JNI global reference).
- */
-class JavaPixelAllocator : public SkBRDAllocator {
+class HeapAllocator : public SkBRDAllocator {
 public:
-    explicit JavaPixelAllocator(JNIEnv* env);
-    ~JavaPixelAllocator();
+   HeapAllocator();
+    ~HeapAllocator();
 
     virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) override;
 
@@ -140,14 +134,8 @@
         return result;
     };
 
-    /**
-     *  Indicates that this allocator allocates zero initialized
-     *  memory.
-     */
     SkCodec::ZeroInitialized zeroInit() const override { return SkCodec::kYes_ZeroInitialized; }
-
 private:
-    JavaVM* mJavaVM;
     android::Bitmap* mStorage = nullptr;
 };
 
diff --git a/core/jni/android/graphics/Matrix.cpp b/core/jni/android/graphics/Matrix.cpp
index b0f3bb4..f8bb77a 100644
--- a/core/jni/android/graphics/Matrix.cpp
+++ b/core/jni/android/graphics/Matrix.cpp
@@ -2,16 +2,16 @@
 **
 ** Copyright 2006, 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 
+** 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 
+**     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 
+** 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.
 */
 
@@ -25,14 +25,25 @@
 
 namespace android {
 
+static_assert(sizeof(SkMatrix) == 40, "Unexpected sizeof(SkMatrix), "
+        "update size in Matrix.java#NATIVE_ALLOCATION_SIZE and here");
+static_assert(SK_SCALAR_IS_FLOAT, "SK_SCALAR_IS_FLOAT is false, "
+        "only float scalar is supported");
+
 class SkMatrixGlue {
 public:
 
-    static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) {
+    // ---------------- Regular JNI -----------------------------
+
+    static void finalizer(jlong objHandle) {
         SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
         delete obj;
     }
 
+    static jlong getNativeFinalizer(JNIEnv* env, jobject clazz) {
+        return static_cast<jlong>(reinterpret_cast<uintptr_t>(&finalizer));
+    }
+
     static jlong create(JNIEnv* env, jobject clazz, jlong srcHandle) {
         const SkMatrix* src = reinterpret_cast<SkMatrix*>(srcHandle);
         SkMatrix* obj = new SkMatrix();
@@ -43,156 +54,39 @@
         return reinterpret_cast<jlong>(obj);
     }
 
-    static jboolean isIdentity(JNIEnv* env, jobject clazz, jlong objHandle) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        return obj->isIdentity() ? JNI_TRUE : JNI_FALSE;
-    }
+    // ---------------- @FastNative -----------------------------
 
-    static jboolean isAffine(JNIEnv* env, jobject clazz, jlong objHandle) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        return obj->asAffine(NULL) ? JNI_TRUE : JNI_FALSE;
-    }
-
-    static jboolean rectStaysRect(JNIEnv* env, jobject clazz, jlong objHandle) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        return obj->rectStaysRect() ? JNI_TRUE : JNI_FALSE;
-    }
-
-    static void reset(JNIEnv* env, jobject clazz, jlong objHandle) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->reset();
-    }
-     static void set(JNIEnv* env, jobject clazz, jlong objHandle, jlong otherHandle) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle);
-        *obj = *other;
-    }
-     static void setTranslate(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->setTranslate(dx, dy);
-    }
-     static void setScale__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy, jfloat px, jfloat py) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->setScale(sx, sy, px, py);
-    }
-     static void setScale__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->setScale(sx, sy);
-    }
-     static void setRotate__FFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees, jfloat px, jfloat py) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->setRotate(degrees, px, py);
-    }
-     static void setRotate__F(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->setRotate(degrees);
-    }
-     static void setSinCos__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sinValue, jfloat cosValue, jfloat px, jfloat py) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->setSinCos(sinValue, cosValue, px, py);
-    }
-     static void setSinCos__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sinValue, jfloat cosValue) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->setSinCos(sinValue, cosValue);
-    }
-     static void setSkew__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky, jfloat px, jfloat py) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->setSkew(kx, ky, px, py);
-    }
-     static void setSkew__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->setSkew(kx, ky);
-    }
-     static void setConcat(JNIEnv* env, jobject clazz, jlong objHandle, jlong aHandle, jlong bHandle) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        SkMatrix* a = reinterpret_cast<SkMatrix*>(aHandle);
-        SkMatrix* b = reinterpret_cast<SkMatrix*>(bHandle);
-        obj->setConcat(*a, *b);
-    }
-
-    static void preTranslate(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->preTranslate(dx, dy);
-    }
-
-    static void preScale__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy, jfloat px, jfloat py) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->preScale(sx, sy, px, py);
-    }
-
-    static void preScale__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->preScale(sx, sy);
-    }
-
-    static void preRotate__FFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees, jfloat px, jfloat py) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->preRotate(degrees, px, py);
-    }
-
-    static void preRotate__F(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->preRotate(degrees);
-    }
-
-    static void preSkew__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky, jfloat px, jfloat py) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->preSkew(kx, ky, px, py);
-    }
-
-    static void preSkew__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->preSkew(kx, ky);
-    }
-
-    static void preConcat(JNIEnv* env, jobject clazz, jlong objHandle, jlong otherHandle) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle);
-        obj->preConcat(*other);
-    }
-
-    static void postTranslate(JNIEnv* env, jobject clazz, jlong objHandle, jfloat dx, jfloat dy) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->postTranslate(dx, dy);
-    }
-
-    static void postScale__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy, jfloat px, jfloat py) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->postScale(sx, sy, px, py);
-    }
-
-    static void postScale__FF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat sx, jfloat sy) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->postScale(sx, sy);
-    }
-
-    static void postRotate__FFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees, jfloat px, jfloat py) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->postRotate(degrees, px, py);
-    }
-
-    static void postRotate__F(JNIEnv* env, jobject clazz, jlong objHandle, jfloat degrees) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->postRotate(degrees);
-    }
-
-    static void postSkew__FFFF(JNIEnv* env, jobject clazz, jlong objHandle, jfloat kx, jfloat ky, jfloat px, jfloat py) {
-        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
-        obj->postSkew(kx, ky, px, py);
-    }
-
-    static void postSkew__FF(JNIEnv* env, jobject clazz, jlong matrixHandle, jfloat kx, jfloat ky) {
+    static void mapPoints(JNIEnv* env, jobject clazz, jlong matrixHandle,
+            jfloatArray dst, jint dstIndex, jfloatArray src, jint srcIndex,
+            jint ptCount, jboolean isPts) {
         SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
-        matrix->postSkew(kx, ky);
+        SkASSERT(ptCount >= 0);
+        AutoJavaFloatArray autoSrc(env, src, srcIndex + (ptCount << 1),
+                kRO_JNIAccess);
+        AutoJavaFloatArray autoDst(env, dst, dstIndex + (ptCount << 1),
+                kRW_JNIAccess);
+        float* srcArray = autoSrc.ptr() + srcIndex;
+        float* dstArray = autoDst.ptr() + dstIndex;
+        if (isPts)
+            matrix->mapPoints((SkPoint*) dstArray, (const SkPoint*) srcArray,
+                    ptCount);
+        else
+            matrix->mapVectors((SkVector*) dstArray, (const SkVector*) srcArray,
+                    ptCount);
     }
 
-    static void postConcat(JNIEnv* env, jobject clazz, jlong matrixHandle, jlong otherHandle) {
+    static jboolean mapRect__RectFRectF(JNIEnv* env, jobject clazz,
+            jlong matrixHandle, jobjectArray dst, jobject src) {
         SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
-        SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle);
-        matrix->postConcat(*other);
+        SkRect dst_, src_;
+        GraphicsJNI::jrectf_to_rect(env, src, &src_);
+        jboolean rectStaysRect = matrix->mapRect(&dst_, src_);
+        GraphicsJNI::rect_to_jrectf(dst_, env, dst);
+        return rectStaysRect ? JNI_TRUE : JNI_FALSE;
     }
 
-    static jboolean setRectToRect(JNIEnv* env, jobject clazz, jlong matrixHandle, jobject src, jobject dst, jint stfHandle) {
+    static jboolean setRectToRect(JNIEnv* env, jobject clazz,
+            jlong matrixHandle, jobject src, jobject dst, jint stfHandle) {
         SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
         SkMatrix::ScaleToFit stf = static_cast<SkMatrix::ScaleToFit>(stfHandle);
         SkRect src_;
@@ -202,150 +96,290 @@
         return matrix->setRectToRect(src_, dst_, stf) ? JNI_TRUE : JNI_FALSE;
     }
 
-    static jboolean setPolyToPoly(JNIEnv* env, jobject clazz, jlong matrixHandle,
-                                  jfloatArray jsrc, jint srcIndex,
-                                  jfloatArray jdst, jint dstIndex, jint ptCount) {
+    static jboolean setPolyToPoly(JNIEnv* env, jobject clazz,
+            jlong matrixHandle, jfloatArray jsrc, jint srcIndex,
+            jfloatArray jdst, jint dstIndex, jint ptCount) {
         SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
         SkASSERT(srcIndex >= 0);
         SkASSERT(dstIndex >= 0);
-        SkASSERT((unsigned)ptCount <= 4);
+        SkASSERT((unsigned )ptCount <= 4);
 
-        AutoJavaFloatArray autoSrc(env, jsrc, srcIndex + (ptCount << 1), kRO_JNIAccess);
-        AutoJavaFloatArray autoDst(env, jdst, dstIndex + (ptCount << 1), kRW_JNIAccess);
+        AutoJavaFloatArray autoSrc(env, jsrc, srcIndex + (ptCount << 1),
+                kRO_JNIAccess);
+        AutoJavaFloatArray autoDst(env, jdst, dstIndex + (ptCount << 1),
+                kRW_JNIAccess);
         float* src = autoSrc.ptr() + srcIndex;
         float* dst = autoDst.ptr() + dstIndex;
         bool result;
 
-#ifdef SK_SCALAR_IS_FLOAT
-        result = matrix->setPolyToPoly((const SkPoint*)src, (const SkPoint*)dst,
-                                     ptCount);
-#else
-        SkASSERT(false);
-#endif
+        result = matrix->setPolyToPoly((const SkPoint*) src,
+                (const SkPoint*) dst, ptCount);
         return result ? JNI_TRUE : JNI_FALSE;
     }
 
-    static jboolean invert(JNIEnv* env, jobject clazz, jlong matrixHandle, jlong inverseHandle) {
+    static void getValues(JNIEnv* env, jobject clazz, jlong matrixHandle,
+            jfloatArray values) {
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        AutoJavaFloatArray autoValues(env, values, 9, kRW_JNIAccess);
+        float* dst = autoValues.ptr();
+        for (int i = 0; i < 9; i++) {
+            dst[i] = matrix->get(i);
+        }
+    }
+
+    static void setValues(JNIEnv* env, jobject clazz, jlong matrixHandle,
+            jfloatArray values) {
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        AutoJavaFloatArray autoValues(env, values, 9, kRO_JNIAccess);
+        const float* src = autoValues.ptr();
+
+        for (int i = 0; i < 9; i++) {
+            matrix->set(i, src[i]);
+        }
+    }
+
+    // ---------------- @CriticalNative -----------------------------
+
+    static jboolean isIdentity(jlong objHandle) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        return obj->isIdentity() ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static jboolean isAffine(jlong objHandle) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        return obj->asAffine(NULL) ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static jboolean rectStaysRect(jlong objHandle) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        return obj->rectStaysRect() ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static void reset(jlong objHandle) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->reset();
+    }
+
+    static void set(jlong objHandle, jlong otherHandle) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle);
+        *obj = *other;
+    }
+
+    static void setTranslate(jlong objHandle, jfloat dx, jfloat dy) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->setTranslate(dx, dy);
+    }
+
+    static void setScale__FFFF(jlong objHandle, jfloat sx, jfloat sy, jfloat px,
+            jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->setScale(sx, sy, px, py);
+    }
+
+    static void setScale__FF(jlong objHandle, jfloat sx, jfloat sy) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->setScale(sx, sy);
+    }
+
+    static void setRotate__FFF(jlong objHandle, jfloat degrees, jfloat px,
+            jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->setRotate(degrees, px, py);
+    }
+
+    static void setRotate__F(jlong objHandle, jfloat degrees) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->setRotate(degrees);
+    }
+
+    static void setSinCos__FFFF(jlong objHandle, jfloat sinValue,
+            jfloat cosValue, jfloat px, jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->setSinCos(sinValue, cosValue, px, py);
+    }
+
+    static void setSinCos__FF(jlong objHandle, jfloat sinValue,
+            jfloat cosValue) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->setSinCos(sinValue, cosValue);
+    }
+
+    static void setSkew__FFFF(jlong objHandle, jfloat kx, jfloat ky, jfloat px,
+            jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->setSkew(kx, ky, px, py);
+    }
+
+    static void setSkew__FF(jlong objHandle, jfloat kx, jfloat ky) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->setSkew(kx, ky);
+    }
+
+    static void setConcat(jlong objHandle, jlong aHandle, jlong bHandle) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        SkMatrix* a = reinterpret_cast<SkMatrix*>(aHandle);
+        SkMatrix* b = reinterpret_cast<SkMatrix*>(bHandle);
+        obj->setConcat(*a, *b);
+    }
+
+    static void preTranslate(jlong objHandle, jfloat dx, jfloat dy) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->preTranslate(dx, dy);
+    }
+
+    static void preScale__FFFF(jlong objHandle, jfloat sx, jfloat sy, jfloat px,
+            jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->preScale(sx, sy, px, py);
+    }
+
+    static void preScale__FF(jlong objHandle, jfloat sx, jfloat sy) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->preScale(sx, sy);
+    }
+
+    static void preRotate__FFF(jlong objHandle, jfloat degrees, jfloat px,
+            jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->preRotate(degrees, px, py);
+    }
+
+    static void preRotate__F(jlong objHandle, jfloat degrees) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->preRotate(degrees);
+    }
+
+    static void preSkew__FFFF(jlong objHandle, jfloat kx, jfloat ky, jfloat px,
+            jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->preSkew(kx, ky, px, py);
+    }
+
+    static void preSkew__FF(jlong objHandle, jfloat kx, jfloat ky) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->preSkew(kx, ky);
+    }
+
+    static void preConcat(jlong objHandle, jlong otherHandle) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle);
+        obj->preConcat(*other);
+    }
+
+    static void postTranslate(jlong objHandle, jfloat dx, jfloat dy) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->postTranslate(dx, dy);
+    }
+
+    static void postScale__FFFF(jlong objHandle, jfloat sx, jfloat sy,
+            jfloat px, jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->postScale(sx, sy, px, py);
+    }
+
+    static void postScale__FF(jlong objHandle, jfloat sx, jfloat sy) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->postScale(sx, sy);
+    }
+
+    static void postRotate__FFF(jlong objHandle, jfloat degrees, jfloat px,
+            jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->postRotate(degrees, px, py);
+    }
+
+    static void postRotate__F(jlong objHandle, jfloat degrees) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->postRotate(degrees);
+    }
+
+    static void postSkew__FFFF(jlong objHandle, jfloat kx, jfloat ky, jfloat px,
+            jfloat py) {
+        SkMatrix* obj = reinterpret_cast<SkMatrix*>(objHandle);
+        obj->postSkew(kx, ky, px, py);
+    }
+
+    static void postSkew__FF(jlong matrixHandle, jfloat kx, jfloat ky) {
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        matrix->postSkew(kx, ky);
+    }
+
+    static void postConcat(jlong matrixHandle, jlong otherHandle) {
+        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
+        SkMatrix* other = reinterpret_cast<SkMatrix*>(otherHandle);
+        matrix->postConcat(*other);
+    }
+
+    static jboolean invert(jlong matrixHandle, jlong inverseHandle) {
         SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
         SkMatrix* inverse = reinterpret_cast<SkMatrix*>(inverseHandle);
         return matrix->invert(inverse);
     }
 
-    static void mapPoints(JNIEnv* env, jobject clazz, jlong matrixHandle,
-                              jfloatArray dst, jint dstIndex,
-                              jfloatArray src, jint srcIndex,
-                              jint ptCount, jboolean isPts) {
-        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
-        SkASSERT(ptCount >= 0);
-        AutoJavaFloatArray autoSrc(env, src, srcIndex + (ptCount << 1), kRO_JNIAccess);
-        AutoJavaFloatArray autoDst(env, dst, dstIndex + (ptCount << 1), kRW_JNIAccess);
-        float* srcArray = autoSrc.ptr() + srcIndex;
-        float* dstArray = autoDst.ptr() + dstIndex;
-#ifdef SK_SCALAR_IS_FLOAT
-        if (isPts)
-            matrix->mapPoints((SkPoint*)dstArray, (const SkPoint*)srcArray,
-                              ptCount);
-        else
-            matrix->mapVectors((SkVector*)dstArray, (const SkVector*)srcArray,
-                               ptCount);
-#else
-        SkASSERT(false);
-#endif
-    }
-
-    static jboolean mapRect__RectFRectF(JNIEnv* env, jobject clazz, jlong matrixHandle, jobjectArray dst, jobject src) {
-        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
-        SkRect dst_, src_;
-        GraphicsJNI::jrectf_to_rect(env, src, &src_);
-        jboolean rectStaysRect = matrix->mapRect(&dst_, src_);
-        GraphicsJNI::rect_to_jrectf(dst_, env, dst);
-        return rectStaysRect ? JNI_TRUE : JNI_FALSE;
-    }
-
-    static jfloat mapRadius(JNIEnv* env, jobject clazz, jlong matrixHandle, jfloat radius) {
+    static jfloat mapRadius(jlong matrixHandle, jfloat radius) {
         SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
         float result;
         result = SkScalarToFloat(matrix->mapRadius(radius));
         return static_cast<jfloat>(result);
     }
-    static void getValues(JNIEnv* env, jobject clazz, jlong matrixHandle, jfloatArray values) {
-        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
-        AutoJavaFloatArray autoValues(env, values, 9, kRW_JNIAccess);
-        float* dst = autoValues.ptr();
-#ifdef SK_SCALAR_IS_FLOAT
-        for (int i = 0; i < 9; i++) {
-            dst[i] = matrix->get(i);
-        }
-#else
-        SkASSERT(false);
-#endif
-    }
 
-    static void setValues(JNIEnv* env, jobject clazz, jlong matrixHandle, jfloatArray values) {
-        SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
-        AutoJavaFloatArray autoValues(env, values, 9, kRO_JNIAccess);
-        const float* src = autoValues.ptr();
-
-#ifdef SK_SCALAR_IS_FLOAT
-        for (int i = 0; i < 9; i++) {
-            matrix->set(i, src[i]);
-        }
-#else
-        SkASSERT(false);
-#endif
-    }
-
-    static jboolean equals(JNIEnv* env, jobject clazz, jlong aHandle, jlong bHandle) {
+    static jboolean equals(jlong aHandle, jlong bHandle) {
         const SkMatrix* a = reinterpret_cast<SkMatrix*>(aHandle);
         const SkMatrix* b = reinterpret_cast<SkMatrix*>(bHandle);
         return *a == *b;
     }
- };
+};
 
 static const JNINativeMethod methods[] = {
-    {"finalizer", "(J)V", (void*) SkMatrixGlue::finalizer},
-    {"native_create","(J)J", (void*) SkMatrixGlue::create},
+    {"nGetNativeFinalizer", "()J", (void*) SkMatrixGlue::getNativeFinalizer},
+    {"nCreate","(J)J", (void*) SkMatrixGlue::create},
 
-    {"native_isIdentity","!(J)Z", (void*) SkMatrixGlue::isIdentity},
-    {"native_isAffine","!(J)Z", (void*) SkMatrixGlue::isAffine},
-    {"native_rectStaysRect","!(J)Z", (void*) SkMatrixGlue::rectStaysRect},
-    {"native_reset","!(J)V", (void*) SkMatrixGlue::reset},
-    {"native_set","!(JJ)V", (void*) SkMatrixGlue::set},
-    {"native_setTranslate","!(JFF)V", (void*) SkMatrixGlue::setTranslate},
-    {"native_setScale","!(JFFFF)V", (void*) SkMatrixGlue::setScale__FFFF},
-    {"native_setScale","!(JFF)V", (void*) SkMatrixGlue::setScale__FF},
-    {"native_setRotate","!(JFFF)V", (void*) SkMatrixGlue::setRotate__FFF},
-    {"native_setRotate","!(JF)V", (void*) SkMatrixGlue::setRotate__F},
-    {"native_setSinCos","!(JFFFF)V", (void*) SkMatrixGlue::setSinCos__FFFF},
-    {"native_setSinCos","!(JFF)V", (void*) SkMatrixGlue::setSinCos__FF},
-    {"native_setSkew","!(JFFFF)V", (void*) SkMatrixGlue::setSkew__FFFF},
-    {"native_setSkew","!(JFF)V", (void*) SkMatrixGlue::setSkew__FF},
-    {"native_setConcat","!(JJJ)V", (void*) SkMatrixGlue::setConcat},
-    {"native_preTranslate","!(JFF)V", (void*) SkMatrixGlue::preTranslate},
-    {"native_preScale","!(JFFFF)V", (void*) SkMatrixGlue::preScale__FFFF},
-    {"native_preScale","!(JFF)V", (void*) SkMatrixGlue::preScale__FF},
-    {"native_preRotate","!(JFFF)V", (void*) SkMatrixGlue::preRotate__FFF},
-    {"native_preRotate","!(JF)V", (void*) SkMatrixGlue::preRotate__F},
-    {"native_preSkew","!(JFFFF)V", (void*) SkMatrixGlue::preSkew__FFFF},
-    {"native_preSkew","!(JFF)V", (void*) SkMatrixGlue::preSkew__FF},
-    {"native_preConcat","!(JJ)V", (void*) SkMatrixGlue::preConcat},
-    {"native_postTranslate","!(JFF)V", (void*) SkMatrixGlue::postTranslate},
-    {"native_postScale","!(JFFFF)V", (void*) SkMatrixGlue::postScale__FFFF},
-    {"native_postScale","!(JFF)V", (void*) SkMatrixGlue::postScale__FF},
-    {"native_postRotate","!(JFFF)V", (void*) SkMatrixGlue::postRotate__FFF},
-    {"native_postRotate","!(JF)V", (void*) SkMatrixGlue::postRotate__F},
-    {"native_postSkew","!(JFFFF)V", (void*) SkMatrixGlue::postSkew__FFFF},
-    {"native_postSkew","!(JFF)V", (void*) SkMatrixGlue::postSkew__FF},
-    {"native_postConcat","!(JJ)V", (void*) SkMatrixGlue::postConcat},
-    {"native_setRectToRect","!(JLandroid/graphics/RectF;Landroid/graphics/RectF;I)Z", (void*) SkMatrixGlue::setRectToRect},
-    {"native_setPolyToPoly","!(J[FI[FII)Z", (void*) SkMatrixGlue::setPolyToPoly},
-    {"native_invert","!(JJ)Z", (void*) SkMatrixGlue::invert},
-    {"native_mapPoints","!(J[FI[FIIZ)V", (void*) SkMatrixGlue::mapPoints},
-    {"native_mapRect","!(JLandroid/graphics/RectF;Landroid/graphics/RectF;)Z", (void*) SkMatrixGlue::mapRect__RectFRectF},
-    {"native_mapRadius","!(JF)F", (void*) SkMatrixGlue::mapRadius},
-    {"native_getValues","!(J[F)V", (void*) SkMatrixGlue::getValues},
-    {"native_setValues","!(J[F)V", (void*) SkMatrixGlue::setValues},
-    {"native_equals", "!(JJ)Z", (void*) SkMatrixGlue::equals}
+    // ------- @FastNative below here ---------------
+    {"nMapPoints","(J[FI[FIIZ)V", (void*) SkMatrixGlue::mapPoints},
+    {"nMapRect","(JLandroid/graphics/RectF;Landroid/graphics/RectF;)Z",
+            (void*) SkMatrixGlue::mapRect__RectFRectF},
+    {"nSetRectToRect","(JLandroid/graphics/RectF;Landroid/graphics/RectF;I)Z",
+            (void*) SkMatrixGlue::setRectToRect},
+    {"nSetPolyToPoly","(J[FI[FII)Z", (void*) SkMatrixGlue::setPolyToPoly},
+    {"nGetValues","(J[F)V", (void*) SkMatrixGlue::getValues},
+    {"nSetValues","(J[F)V", (void*) SkMatrixGlue::setValues},
+
+    // ------- @CriticalNative below here ---------------
+    {"nIsIdentity","(J)Z", (void*) SkMatrixGlue::isIdentity},
+    {"nIsAffine","(J)Z", (void*) SkMatrixGlue::isAffine},
+    {"nRectStaysRect","(J)Z", (void*) SkMatrixGlue::rectStaysRect},
+    {"nReset","(J)V", (void*) SkMatrixGlue::reset},
+    {"nSet","(JJ)V", (void*) SkMatrixGlue::set},
+    {"nSetTranslate","(JFF)V", (void*) SkMatrixGlue::setTranslate},
+    {"nSetScale","(JFFFF)V", (void*) SkMatrixGlue::setScale__FFFF},
+    {"nSetScale","(JFF)V", (void*) SkMatrixGlue::setScale__FF},
+    {"nSetRotate","(JFFF)V", (void*) SkMatrixGlue::setRotate__FFF},
+    {"nSetRotate","(JF)V", (void*) SkMatrixGlue::setRotate__F},
+    {"nSetSinCos","(JFFFF)V", (void*) SkMatrixGlue::setSinCos__FFFF},
+    {"nSetSinCos","(JFF)V", (void*) SkMatrixGlue::setSinCos__FF},
+    {"nSetSkew","(JFFFF)V", (void*) SkMatrixGlue::setSkew__FFFF},
+    {"nSetSkew","(JFF)V", (void*) SkMatrixGlue::setSkew__FF},
+    {"nSetConcat","(JJJ)V", (void*) SkMatrixGlue::setConcat},
+    {"nPreTranslate","(JFF)V", (void*) SkMatrixGlue::preTranslate},
+    {"nPreScale","(JFFFF)V", (void*) SkMatrixGlue::preScale__FFFF},
+    {"nPreScale","(JFF)V", (void*) SkMatrixGlue::preScale__FF},
+    {"nPreRotate","(JFFF)V", (void*) SkMatrixGlue::preRotate__FFF},
+    {"nPreRotate","(JF)V", (void*) SkMatrixGlue::preRotate__F},
+    {"nPreSkew","(JFFFF)V", (void*) SkMatrixGlue::preSkew__FFFF},
+    {"nPreSkew","(JFF)V", (void*) SkMatrixGlue::preSkew__FF},
+    {"nPreConcat","(JJ)V", (void*) SkMatrixGlue::preConcat},
+    {"nPostTranslate","(JFF)V", (void*) SkMatrixGlue::postTranslate},
+    {"nPostScale","(JFFFF)V", (void*) SkMatrixGlue::postScale__FFFF},
+    {"nPostScale","(JFF)V", (void*) SkMatrixGlue::postScale__FF},
+    {"nPostRotate","(JFFF)V", (void*) SkMatrixGlue::postRotate__FFF},
+    {"nPostRotate","(JF)V", (void*) SkMatrixGlue::postRotate__F},
+    {"nPostSkew","(JFFFF)V", (void*) SkMatrixGlue::postSkew__FFFF},
+    {"nPostSkew","(JFF)V", (void*) SkMatrixGlue::postSkew__FF},
+    {"nPostConcat","(JJ)V", (void*) SkMatrixGlue::postConcat},
+    {"nInvert","(JJ)Z", (void*) SkMatrixGlue::invert},
+    {"nMapRadius","(JF)F", (void*) SkMatrixGlue::mapRadius},
+    {"nEquals", "(JJ)Z", (void*) SkMatrixGlue::equals}
 };
 
 static jfieldID sNativeInstanceField;
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 315dd6b..4b1530a 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -105,392 +105,80 @@
         return reinterpret_cast<jlong>(obj);
     }
 
-    static void reset(JNIEnv* env, jobject clazz, jlong objHandle) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        obj->reset();
-        defaultSettingsForAndroid(obj);
-    }
+    static int breakText(JNIEnv* env, const Paint& paint, Typeface* typeface, const jchar text[],
+            int count, float maxWidth, jint bidiFlags, jfloatArray jmeasured,
+            const bool forwardScan) {
+        size_t measuredCount = 0;
+        float measured = 0;
 
-    static void assign(JNIEnv* env, jobject clazz, jlong dstPaintHandle, jlong srcPaintHandle) {
-        Paint* dst = reinterpret_cast<Paint*>(dstPaintHandle);
-        const Paint* src = reinterpret_cast<Paint*>(srcPaintHandle);
-        *dst = *src;
-    }
+        std::unique_ptr<float[]> advancesArray(new float[count]);
+        MinikinUtils::measureText(&paint, bidiFlags, typeface, text, 0, count, count,
+                advancesArray.get());
 
-    // Equivalent to the Java Paint's FILTER_BITMAP_FLAG.
-    static const uint32_t sFilterBitmapFlag = 0x02;
-
-    static jint getFlags(JNIEnv* env, jobject, jlong paintHandle) {
-        Paint* nativePaint = reinterpret_cast<Paint*>(paintHandle);
-        uint32_t result = nativePaint->getFlags();
-        result &= ~sFilterBitmapFlag; // Filtering no longer stored in this bit. Mask away.
-        if (nativePaint->getFilterQuality() != kNone_SkFilterQuality) {
-            result |= sFilterBitmapFlag;
+        for (int i = 0; i < count; i++) {
+            // traverse in the given direction
+            int index = forwardScan ? i : (count - i - 1);
+            float width = advancesArray[index];
+            if (measured + width > maxWidth) {
+                break;
+            }
+            // properly handle clusters when scanning backwards
+            if (forwardScan || width != 0.0f) {
+                measuredCount = i + 1;
+            }
+            measured += width;
         }
-        return static_cast<jint>(result);
-    }
 
-    static void setFlags(JNIEnv* env, jobject, jlong paintHandle, jint flags) {
-        Paint* nativePaint = reinterpret_cast<Paint*>(paintHandle);
-        // Instead of modifying 0x02, change the filter level.
-        nativePaint->setFilterQuality(flags & sFilterBitmapFlag
-                ? kLow_SkFilterQuality
-                : kNone_SkFilterQuality);
-        // Don't pass through filter flag, which is no longer stored in paint's flags.
-        flags &= ~sFilterBitmapFlag;
-        // Use the existing value for 0x02.
-        const uint32_t existing0x02Flag = nativePaint->getFlags() & sFilterBitmapFlag;
-        flags |= existing0x02Flag;
-        nativePaint->setFlags(flags);
-    }
-
-    static jint getHinting(JNIEnv* env, jobject, jlong paintHandle) {
-        return reinterpret_cast<Paint*>(paintHandle)->getHinting()
-                == Paint::kNo_Hinting ? 0 : 1;
-    }
-
-    static void setHinting(JNIEnv* env, jobject, jlong paintHandle, jint mode) {
-        reinterpret_cast<Paint*>(paintHandle)->setHinting(
-                mode == 0 ? Paint::kNo_Hinting : Paint::kNormal_Hinting);
-    }
-
-    static void setAntiAlias(JNIEnv* env, jobject, jlong paintHandle, jboolean aa) {
-        reinterpret_cast<Paint*>(paintHandle)->setAntiAlias(aa);
-    }
-
-    static void setLinearText(JNIEnv* env, jobject, jlong paintHandle, jboolean linearText) {
-        reinterpret_cast<Paint*>(paintHandle)->setLinearText(linearText);
-    }
-
-    static void setSubpixelText(JNIEnv* env, jobject, jlong paintHandle, jboolean subpixelText) {
-        reinterpret_cast<Paint*>(paintHandle)->setSubpixelText(subpixelText);
-    }
-
-    static void setUnderlineText(JNIEnv* env, jobject, jlong paintHandle, jboolean underlineText) {
-        reinterpret_cast<Paint*>(paintHandle)->setUnderlineText(underlineText);
-    }
-
-    static void setStrikeThruText(JNIEnv* env, jobject, jlong paintHandle, jboolean strikeThruText) {
-        reinterpret_cast<Paint*>(paintHandle)->setStrikeThruText(strikeThruText);
-    }
-
-    static void setFakeBoldText(JNIEnv* env, jobject, jlong paintHandle, jboolean fakeBoldText) {
-        reinterpret_cast<Paint*>(paintHandle)->setFakeBoldText(fakeBoldText);
-    }
-
-    static void setFilterBitmap(JNIEnv* env, jobject, jlong paintHandle, jboolean filterBitmap) {
-        reinterpret_cast<Paint*>(paintHandle)->setFilterQuality(
-                filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality);
-    }
-
-    static void setDither(JNIEnv* env, jobject, jlong paintHandle, jboolean dither) {
-        reinterpret_cast<Paint*>(paintHandle)->setDither(dither);
-    }
-
-    static jint getStyle(JNIEnv* env, jobject clazz,jlong objHandle) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        return static_cast<jint>(obj->getStyle());
-    }
-
-    static void setStyle(JNIEnv* env, jobject clazz, jlong objHandle, jint styleHandle) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        Paint::Style style = static_cast<Paint::Style>(styleHandle);
-        obj->setStyle(style);
-    }
-
-    static jint getColor(JNIEnv* env, jobject, jlong paintHandle) {
-        int color;
-        color = reinterpret_cast<Paint*>(paintHandle)->getColor();
-        return static_cast<jint>(color);
-    }
-
-    static jint getAlpha(JNIEnv* env, jobject, jlong paintHandle) {
-        int alpha;
-        alpha = reinterpret_cast<Paint*>(paintHandle)->getAlpha();
-        return static_cast<jint>(alpha);
-    }
-
-    static void setColor(JNIEnv* env, jobject, jlong paintHandle, jint color) {
-        reinterpret_cast<Paint*>(paintHandle)->setColor(color);
-    }
-
-    static void setAlpha(JNIEnv* env, jobject, jlong paintHandle, jint a) {
-        reinterpret_cast<Paint*>(paintHandle)->setAlpha(a);
-    }
-
-    static jfloat getStrokeWidth(JNIEnv* env, jobject, jlong paintHandle) {
-        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeWidth());
-    }
-
-    static void setStrokeWidth(JNIEnv* env, jobject, jlong paintHandle, jfloat width) {
-        reinterpret_cast<Paint*>(paintHandle)->setStrokeWidth(width);
-    }
-
-    static jfloat getStrokeMiter(JNIEnv* env, jobject, jlong paintHandle) {
-        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeMiter());
-    }
-
-    static void setStrokeMiter(JNIEnv* env, jobject, jlong paintHandle, jfloat miter) {
-        reinterpret_cast<Paint*>(paintHandle)->setStrokeMiter(miter);
-    }
-
-    static jint getStrokeCap(JNIEnv* env, jobject clazz, jlong objHandle) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        return static_cast<jint>(obj->getStrokeCap());
-    }
-
-    static void setStrokeCap(JNIEnv* env, jobject clazz, jlong objHandle, jint capHandle) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        Paint::Cap cap = static_cast<Paint::Cap>(capHandle);
-        obj->setStrokeCap(cap);
-    }
-
-    static jint getStrokeJoin(JNIEnv* env, jobject clazz, jlong objHandle) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        return static_cast<jint>(obj->getStrokeJoin());
-    }
-
-    static void setStrokeJoin(JNIEnv* env, jobject clazz, jlong objHandle, jint joinHandle) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        Paint::Join join = (Paint::Join) joinHandle;
-        obj->setStrokeJoin(join);
-    }
-
-    static jboolean getFillPath(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle, jlong dstHandle) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
-        SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
-        return obj->getFillPath(*src, dst) ? JNI_TRUE : JNI_FALSE;
-    }
-
-    static jlong setShader(JNIEnv* env, jobject clazz, jlong objHandle, jlong shaderHandle) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
-        return reinterpret_cast<jlong>(obj->setShader(shader));
-    }
-
-    static jlong setColorFilter(JNIEnv* env, jobject clazz, jlong objHandle, jlong filterHandle) {
-        Paint* obj = reinterpret_cast<Paint *>(objHandle);
-        SkColorFilter* filter  = reinterpret_cast<SkColorFilter *>(filterHandle);
-        return reinterpret_cast<jlong>(obj->setColorFilter(filter));
-    }
-
-    static void setXfermode(JNIEnv* env, jobject clazz, jlong paintHandle, jint xfermodeHandle) {
-        // validate that the Java enum values match our expectations
-        static_assert(0 == SkXfermode::kClear_Mode, "xfermode_mismatch");
-        static_assert(1 == SkXfermode::kSrc_Mode, "xfermode_mismatch");
-        static_assert(2 == SkXfermode::kDst_Mode, "xfermode_mismatch");
-        static_assert(3 == SkXfermode::kSrcOver_Mode, "xfermode_mismatch");
-        static_assert(4 == SkXfermode::kDstOver_Mode, "xfermode_mismatch");
-        static_assert(5 == SkXfermode::kSrcIn_Mode, "xfermode_mismatch");
-        static_assert(6 == SkXfermode::kDstIn_Mode, "xfermode_mismatch");
-        static_assert(7 == SkXfermode::kSrcOut_Mode, "xfermode_mismatch");
-        static_assert(8 == SkXfermode::kDstOut_Mode, "xfermode_mismatch");
-        static_assert(9 == SkXfermode::kSrcATop_Mode, "xfermode_mismatch");
-        static_assert(10 == SkXfermode::kDstATop_Mode, "xfermode_mismatch");
-        static_assert(11 == SkXfermode::kXor_Mode, "xfermode_mismatch");
-        static_assert(16 == SkXfermode::kDarken_Mode, "xfermode_mismatch");
-        static_assert(17 == SkXfermode::kLighten_Mode, "xfermode_mismatch");
-        static_assert(13 == SkXfermode::kModulate_Mode, "xfermode_mismatch");
-        static_assert(14 == SkXfermode::kScreen_Mode, "xfermode_mismatch");
-        static_assert(12 == SkXfermode::kPlus_Mode, "xfermode_mismatch");
-        static_assert(15 == SkXfermode::kOverlay_Mode, "xfermode_mismatch");
-
-        SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(xfermodeHandle);
-        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        paint->setXfermodeMode(mode);
-    }
-
-    static jlong setPathEffect(JNIEnv* env, jobject clazz, jlong objHandle, jlong effectHandle) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        SkPathEffect* effect  = reinterpret_cast<SkPathEffect*>(effectHandle);
-        return reinterpret_cast<jlong>(obj->setPathEffect(effect));
-    }
-
-    static jlong setMaskFilter(JNIEnv* env, jobject clazz, jlong objHandle, jlong maskfilterHandle) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        SkMaskFilter* maskfilter  = reinterpret_cast<SkMaskFilter*>(maskfilterHandle);
-        return reinterpret_cast<jlong>(obj->setMaskFilter(maskfilter));
-    }
-
-    static jlong setTypeface(JNIEnv* env, jobject clazz, jlong objHandle, jlong typefaceHandle) {
-        // TODO: in Paint refactoring, set typeface on android Paint, not Paint
-        return NULL;
-    }
-
-    static jlong setRasterizer(JNIEnv* env, jobject clazz, jlong objHandle, jlong rasterizerHandle) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        SkAutoTUnref<SkRasterizer> rasterizer(GraphicsJNI::refNativeRasterizer(rasterizerHandle));
-        return reinterpret_cast<jlong>(obj->setRasterizer(rasterizer));
-    }
-
-    static jint getTextAlign(JNIEnv* env, jobject clazz, jlong objHandle) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        return static_cast<jint>(obj->getTextAlign());
-    }
-
-    static void setTextAlign(JNIEnv* env, jobject clazz, jlong objHandle, jint alignHandle) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        Paint::Align align = static_cast<Paint::Align>(alignHandle);
-        obj->setTextAlign(align);
-    }
-
-    static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        ScopedUtfChars localesChars(env, locales);
-        jint minikinLangListId = minikin::FontStyle::registerLanguageList(localesChars.c_str());
-        obj->setMinikinLangListId(minikinLangListId);
-        return minikinLangListId;
-    }
-
-    static void setTextLocalesByMinikinLangListId(JNIEnv* env, jobject clazz, jlong objHandle,
-            jint minikinLangListId) {
-        Paint* obj = reinterpret_cast<Paint*>(objHandle);
-        obj->setMinikinLangListId(minikinLangListId);
-    }
-
-    static jboolean isElegantTextHeight(JNIEnv* env, jobject, jlong paintHandle) {
-        Paint* obj = reinterpret_cast<Paint*>(paintHandle);
-        return obj->getFontVariant() == minikin::VARIANT_ELEGANT;
-    }
-
-    static void setElegantTextHeight(JNIEnv* env, jobject, jlong paintHandle, jboolean aa) {
-        Paint* obj = reinterpret_cast<Paint*>(paintHandle);
-        obj->setFontVariant(aa ? minikin::VARIANT_ELEGANT : minikin::VARIANT_DEFAULT);
-    }
-
-    static jfloat getTextSize(JNIEnv* env, jobject, jlong paintHandle) {
-        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextSize());
-    }
-
-    static void setTextSize(JNIEnv* env, jobject, jlong paintHandle, jfloat textSize) {
-        reinterpret_cast<Paint*>(paintHandle)->setTextSize(textSize);
-    }
-
-    static jfloat getTextScaleX(JNIEnv* env, jobject, jlong paintHandle) {
-        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextScaleX());
-    }
-
-    static void setTextScaleX(JNIEnv* env, jobject, jlong paintHandle, jfloat scaleX) {
-        reinterpret_cast<Paint*>(paintHandle)->setTextScaleX(scaleX);
-    }
-
-    static jfloat getTextSkewX(JNIEnv* env, jobject, jlong paintHandle) {
-        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextSkewX());
-    }
-
-    static void setTextSkewX(JNIEnv* env, jobject, jlong paintHandle, jfloat skewX) {
-        reinterpret_cast<Paint*>(paintHandle)->setTextSkewX(skewX);
-    }
-
-    static jfloat getLetterSpacing(JNIEnv* env, jobject clazz, jlong paintHandle) {
-        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        return paint->getLetterSpacing();
-    }
-
-    static void setLetterSpacing(JNIEnv* env, jobject clazz, jlong paintHandle, jfloat letterSpacing) {
-        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        paint->setLetterSpacing(letterSpacing);
-    }
-
-    static void setFontFeatureSettings(JNIEnv* env, jobject clazz, jlong paintHandle, jstring settings) {
-        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        if (!settings) {
-            paint->setFontFeatureSettings(std::string());
-        } else {
-            ScopedUtfChars settingsChars(env, settings);
-            paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size()));
+        if (jmeasured && env->GetArrayLength(jmeasured) > 0) {
+            AutoJavaFloatArray autoMeasured(env, jmeasured, 1);
+            jfloat* array = autoMeasured.ptr();
+            array[0] = measured;
         }
+        return measuredCount;
     }
 
-    static jint getHyphenEdit(JNIEnv* env, jobject clazz, jlong paintHandle, jint hyphen) {
-        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        return paint->getHyphenEdit();
-    }
+    static jint breakTextC(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle,
+            jcharArray jtext, jint index, jint count, jfloat maxWidth, jint bidiFlags,
+            jfloatArray jmeasuredWidth) {
+        NPE_CHECK_RETURN_ZERO(env, jtext);
 
-    static void setHyphenEdit(JNIEnv* env, jobject clazz, jlong paintHandle, jint hyphen) {
-        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        paint->setHyphenEdit((uint32_t)hyphen);
-    }
-
-    static SkScalar getMetricsInternal(jlong paintHandle, jlong typefaceHandle,
-            Paint::FontMetrics *metrics) {
-        const int kElegantTop = 2500;
-        const int kElegantBottom = -1000;
-        const int kElegantAscent = 1900;
-        const int kElegantDescent = -500;
-        const int kElegantLeading = 0;
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
         Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
-        typeface = Typeface::resolveDefault(typeface);
-        minikin::FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
-        float saveSkewX = paint->getTextSkewX();
-        bool savefakeBold = paint->isFakeBoldText();
-        MinikinFontSkia::populateSkPaint(paint, baseFont.font, baseFont.fakery);
-        SkScalar spacing = paint->getFontMetrics(metrics);
-        // The populateSkPaint call may have changed fake bold / text skew
-        // because we want to measure with those effects applied, so now
-        // restore the original settings.
-        paint->setTextSkewX(saveSkewX);
-        paint->setFakeBoldText(savefakeBold);
-        if (paint->getFontVariant() == minikin::VARIANT_ELEGANT) {
-            SkScalar size = paint->getTextSize();
-            metrics->fTop = -size * kElegantTop / 2048;
-            metrics->fBottom = -size * kElegantBottom / 2048;
-            metrics->fAscent = -size * kElegantAscent / 2048;
-            metrics->fDescent = -size * kElegantDescent / 2048;
-            metrics->fLeading = size * kElegantLeading / 2048;
-            spacing = metrics->fDescent - metrics->fAscent + metrics->fLeading;
+
+        bool forwardTextDirection;
+        if (count < 0) {
+            forwardTextDirection = false;
+            count = -count;
         }
-        return spacing;
-    }
-
-    static jfloat ascent(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle) {
-        Paint::FontMetrics metrics;
-        getMetricsInternal(paintHandle, typefaceHandle, &metrics);
-        return SkScalarToFloat(metrics.fAscent);
-    }
-
-    static jfloat descent(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle) {
-        Paint::FontMetrics metrics;
-        getMetricsInternal(paintHandle, typefaceHandle, &metrics);
-        return SkScalarToFloat(metrics.fDescent);
-    }
-
-    static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle,
-            jlong typefaceHandle, jobject metricsObj) {
-        Paint::FontMetrics metrics;
-        SkScalar spacing = getMetricsInternal(paintHandle, typefaceHandle, &metrics);
-
-        if (metricsObj) {
-            SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class));
-            env->SetFloatField(metricsObj, gFontMetrics_fieldID.top, SkScalarToFloat(metrics.fTop));
-            env->SetFloatField(metricsObj, gFontMetrics_fieldID.ascent, SkScalarToFloat(metrics.fAscent));
-            env->SetFloatField(metricsObj, gFontMetrics_fieldID.descent, SkScalarToFloat(metrics.fDescent));
-            env->SetFloatField(metricsObj, gFontMetrics_fieldID.bottom, SkScalarToFloat(metrics.fBottom));
-            env->SetFloatField(metricsObj, gFontMetrics_fieldID.leading, SkScalarToFloat(metrics.fLeading));
+        else {
+            forwardTextDirection = true;
         }
-        return SkScalarToFloat(spacing);
+
+        if ((index < 0) || (index + count > env->GetArrayLength(jtext))) {
+            doThrowAIOOBE(env);
+            return 0;
+        }
+
+        const jchar* text = env->GetCharArrayElements(jtext, nullptr);
+        count = breakText(env, *paint, typeface, text + index, count, maxWidth,
+                          bidiFlags, jmeasuredWidth, forwardTextDirection);
+        env->ReleaseCharArrayElements(jtext, const_cast<jchar*>(text),
+                                      JNI_ABORT);
+        return count;
     }
 
-    static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle,
-            jlong typefaceHandle, jobject metricsObj) {
-        Paint::FontMetrics metrics;
+    static jint breakTextS(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jstring jtext,
+                jboolean forwards, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
+        NPE_CHECK_RETURN_ZERO(env, jtext);
 
-        getMetricsInternal(paintHandle, typefaceHandle, &metrics);
-        int ascent = SkScalarRoundToInt(metrics.fAscent);
-        int descent = SkScalarRoundToInt(metrics.fDescent);
-        int leading = SkScalarRoundToInt(metrics.fLeading);
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
 
-        if (metricsObj) {
-            SkASSERT(env->IsInstanceOf(metricsObj, gFontMetricsInt_class));
-            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.top, SkScalarFloorToInt(metrics.fTop));
-            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.ascent, ascent);
-            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.descent, descent);
-            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.bottom, SkScalarCeilToInt(metrics.fBottom));
-            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.leading, leading);
-        }
-        return descent - ascent + leading;
+        int count = env->GetStringLength(jtext);
+        const jchar* text = env->GetStringChars(jtext, nullptr);
+        count = breakText(env, *paint, typeface, text, count, maxWidth, bidiFlags, jmeasuredWidth, forwards);
+        env->ReleaseStringChars(jtext, text);
+        return count;
     }
 
     static jfloat doTextAdvances(JNIEnv *env, Paint *paint, Typeface* typeface,
@@ -530,7 +218,7 @@
             jint bidiFlags, jfloatArray advances, jint advancesIndex) {
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
         Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
-        jchar* textArray = env->GetCharArrayElements(text, NULL);
+        jchar* textArray = env->GetCharArrayElements(text, nullptr);
         jfloat result = doTextAdvances(env, paint, typeface, textArray + contextIndex,
                 index - contextIndex, count, contextCount, bidiFlags, advances, advancesIndex);
         env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
@@ -543,7 +231,7 @@
             jfloatArray advances, jint advancesIndex) {
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
         Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
-        const jchar* textArray = env->GetStringChars(text, NULL);
+        const jchar* textArray = env->GetStringChars(text, nullptr);
         jfloat result = doTextAdvances(env, paint, typeface, textArray + contextStart,
                 start - contextStart, end - start, contextEnd - contextStart, bidiFlags,
                 advances, advancesIndex);
@@ -562,7 +250,7 @@
     static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray text,
             jint contextStart, jint contextCount, jint dir, jint offset, jint cursorOpt) {
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        jchar* textArray = env->GetCharArrayElements(text, NULL);
+        jchar* textArray = env->GetCharArrayElements(text, nullptr);
         jint result = doTextRunCursor(env, paint, textArray, contextStart, contextCount, dir,
                 offset, cursorOpt);
         env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
@@ -572,7 +260,7 @@
     static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, jlong paintHandle, jstring text,
             jint contextStart, jint contextEnd, jint dir, jint offset, jint cursorOpt) {
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        const jchar* textArray = env->GetStringChars(text, NULL);
+        const jchar* textArray = env->GetStringChars(text, nullptr);
         jint result = doTextRunCursor(env, paint, textArray, contextStart,
                 contextEnd - contextStart, dir, offset, cursorOpt);
         env->ReleaseStringChars(text, textArray);
@@ -635,7 +323,7 @@
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
         Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
         SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
-        const jchar* textArray = env->GetCharArrayElements(text, NULL);
+        const jchar* textArray = env->GetCharArrayElements(text, nullptr);
         getTextPath(env, paint, typeface, textArray + index, count, bidiFlags, x, y, path);
         env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
     }
@@ -646,103 +334,11 @@
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
         Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
         SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
-        const jchar* textArray = env->GetStringChars(text, NULL);
+        const jchar* textArray = env->GetStringChars(text, nullptr);
         getTextPath(env, paint, typeface, textArray + start, end - start, bidiFlags, x, y, path);
         env->ReleaseStringChars(text, textArray);
     }
 
-    static void setShadowLayer(JNIEnv* env, jobject clazz, jlong paintHandle, jfloat radius,
-                               jfloat dx, jfloat dy, jint color) {
-        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        if (radius <= 0) {
-            paint->setLooper(NULL);
-        }
-        else {
-            SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
-            paint->setLooper(SkBlurDrawLooper::Create((SkColor)color, sigma, dx, dy))->unref();
-        }
-    }
-
-    static jboolean hasShadowLayer(JNIEnv* env, jobject clazz, jlong paintHandle) {
-        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        return paint->getLooper() && paint->getLooper()->asABlurShadow(NULL);
-    }
-
-    static int breakText(JNIEnv* env, const Paint& paint, Typeface* typeface, const jchar text[],
-                         int count, float maxWidth, jint bidiFlags, jfloatArray jmeasured,
-                         const bool forwardScan) {
-        size_t measuredCount = 0;
-        float measured = 0;
-
-        std::unique_ptr<float[]> advancesArray(new float[count]);
-        MinikinUtils::measureText(&paint, bidiFlags, typeface, text, 0, count, count,
-                advancesArray.get());
-
-        for (int i = 0; i < count; i++) {
-            // traverse in the given direction
-            int index = forwardScan ? i : (count - i - 1);
-            float width = advancesArray[index];
-            if (measured + width > maxWidth) {
-                break;
-            }
-            // properly handle clusters when scanning backwards
-            if (forwardScan || width != 0.0f) {
-                measuredCount = i + 1;
-            }
-            measured += width;
-        }
-
-        if (jmeasured && env->GetArrayLength(jmeasured) > 0) {
-            AutoJavaFloatArray autoMeasured(env, jmeasured, 1);
-            jfloat* array = autoMeasured.ptr();
-            array[0] = measured;
-        }
-        return measuredCount;
-    }
-
-    static jint breakTextC(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jcharArray jtext,
-            jint index, jint count, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
-        NPE_CHECK_RETURN_ZERO(env, jtext);
-
-        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
-
-        bool forwardTextDirection;
-        if (count < 0) {
-            forwardTextDirection = false;
-            count = -count;
-        }
-        else {
-            forwardTextDirection = true;
-        }
-
-        if ((index < 0) || (index + count > env->GetArrayLength(jtext))) {
-            doThrowAIOOBE(env);
-            return 0;
-        }
-
-        const jchar* text = env->GetCharArrayElements(jtext, NULL);
-        count = breakText(env, *paint, typeface, text + index, count, maxWidth,
-                          bidiFlags, jmeasuredWidth, forwardTextDirection);
-        env->ReleaseCharArrayElements(jtext, const_cast<jchar*>(text),
-                                      JNI_ABORT);
-        return count;
-    }
-
-    static jint breakTextS(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jstring jtext,
-                jboolean forwards, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
-        NPE_CHECK_RETURN_ZERO(env, jtext);
-
-        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
-        Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
-
-        int count = env->GetStringLength(jtext);
-        const jchar* text = env->GetStringChars(jtext, NULL);
-        count = breakText(env, *paint, typeface, text, count, maxWidth, bidiFlags, jmeasuredWidth, forwards);
-        env->ReleaseStringChars(jtext, text);
-        return count;
-    }
-
     static void doTextBounds(JNIEnv* env, const jchar* text, int count, jobject bounds,
             const Paint& paint, Typeface* typeface, jint bidiFlags) {
         SkRect  r;
@@ -764,7 +360,7 @@
                                 jstring text, jint start, jint end, jint bidiFlags, jobject bounds) {
         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
         Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
-        const jchar* textArray = env->GetStringChars(text, NULL);
+        const jchar* textArray = env->GetStringChars(text, nullptr);
         doTextBounds(env, textArray + start, end - start, bounds, *paint, typeface, bidiFlags);
         env->ReleaseStringChars(text, textArray);
     }
@@ -773,12 +369,28 @@
                         jcharArray text, jint index, jint count, jint bidiFlags, jobject bounds) {
         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
         Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
-        const jchar* textArray = env->GetCharArrayElements(text, NULL);
+        const jchar* textArray = env->GetCharArrayElements(text, nullptr);
         doTextBounds(env, textArray + index, count, bounds, *paint, typeface, bidiFlags);
         env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
                                       JNI_ABORT);
     }
 
+    // Returns true if the given string is exact one pair of regional indicators.
+    static bool isFlag(const jchar* str, size_t length) {
+        const jchar RI_LEAD_SURROGATE = 0xD83C;
+        const jchar RI_TRAIL_SURROGATE_MIN = 0xDDE6;
+        const jchar RI_TRAIL_SURROGATE_MAX = 0xDDFF;
+
+        if (length != 4) {
+            return false;
+        }
+        if (str[0] != RI_LEAD_SURROGATE || str[2] != RI_LEAD_SURROGATE) {
+            return false;
+        }
+        return RI_TRAIL_SURROGATE_MIN <= str[1] && str[1] <= RI_TRAIL_SURROGATE_MAX &&
+            RI_TRAIL_SURROGATE_MIN <= str[3] && str[3] <= RI_TRAIL_SURROGATE_MAX;
+    }
+
     static jboolean layoutContainsNotdef(const minikin::Layout& layout) {
         for (size_t i = 0; i < layout.nGlyphs(); i++) {
             if (layout.getGlyphId(i) == 0) {
@@ -803,22 +415,6 @@
         return count;
     }
 
-    // Returns true if the given string is exact one pair of regional indicators.
-    static bool isFlag(const jchar* str, size_t length) {
-        const jchar RI_LEAD_SURROGATE = 0xD83C;
-        const jchar RI_TRAIL_SURROGATE_MIN = 0xDDE6;
-        const jchar RI_TRAIL_SURROGATE_MAX = 0xDDFF;
-
-        if (length != 4) {
-            return false;
-        }
-        if (str[0] != RI_LEAD_SURROGATE || str[2] != RI_LEAD_SURROGATE) {
-            return false;
-        }
-        return RI_TRAIL_SURROGATE_MIN <= str[1] && str[1] <= RI_TRAIL_SURROGATE_MAX &&
-            RI_TRAIL_SURROGATE_MIN <= str[3] && str[3] <= RI_TRAIL_SURROGATE_MAX;
-    }
-
     static jboolean hasGlyph(JNIEnv *env, jclass, jlong paintHandle, jlong typefaceHandle,
             jint bidiFlags, jstring string) {
         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
@@ -913,7 +509,7 @@
             jint contextEnd, jboolean isRtl, jint offset) {
         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
         Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
-        jchar* textArray = (jchar*) env->GetPrimitiveArrayCritical(text, NULL);
+        jchar* textArray = (jchar*) env->GetPrimitiveArrayCritical(text, nullptr);
         jfloat result = doRunAdvance(paint, typeface, textArray + contextStart,
                 start - contextStart, end - start, contextEnd - contextStart, isRtl,
                 offset - contextStart);
@@ -935,7 +531,7 @@
             jint contextEnd, jboolean isRtl, jfloat advance) {
         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
         Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
-        jchar* textArray = (jchar*) env->GetPrimitiveArrayCritical(text, NULL);
+        jchar* textArray = (jchar*) env->GetPrimitiveArrayCritical(text, nullptr);
         jint result = doOffsetForAdvance(paint, typeface, textArray + contextStart,
                 start - contextStart, end - start, contextEnd - contextStart, isRtl, advance);
         result += contextStart;
@@ -943,76 +539,422 @@
         return result;
     }
 
+    // ------------------ @FastNative ---------------------------
+
+    static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        ScopedUtfChars localesChars(env, locales);
+        jint minikinLangListId = minikin::FontStyle::registerLanguageList(localesChars.c_str());
+        obj->setMinikinLangListId(minikinLangListId);
+        return minikinLangListId;
+    }
+
+    static void setFontFeatureSettings(JNIEnv* env, jobject clazz, jlong paintHandle, jstring settings) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        if (!settings) {
+            paint->setFontFeatureSettings(std::string());
+        } else {
+            ScopedUtfChars settingsChars(env, settings);
+            paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size()));
+        }
+    }
+
+    static SkScalar getMetricsInternal(jlong paintHandle, jlong typefaceHandle,
+            Paint::FontMetrics *metrics) {
+        const int kElegantTop = 2500;
+        const int kElegantBottom = -1000;
+        const int kElegantAscent = 1900;
+        const int kElegantDescent = -500;
+        const int kElegantLeading = 0;
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
+        typeface = Typeface::resolveDefault(typeface);
+        minikin::FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
+        float saveSkewX = paint->getTextSkewX();
+        bool savefakeBold = paint->isFakeBoldText();
+        MinikinFontSkia::populateSkPaint(paint, baseFont.font, baseFont.fakery);
+        SkScalar spacing = paint->getFontMetrics(metrics);
+        // The populateSkPaint call may have changed fake bold / text skew
+        // because we want to measure with those effects applied, so now
+        // restore the original settings.
+        paint->setTextSkewX(saveSkewX);
+        paint->setFakeBoldText(savefakeBold);
+        if (paint->getFontVariant() == minikin::VARIANT_ELEGANT) {
+            SkScalar size = paint->getTextSize();
+            metrics->fTop = -size * kElegantTop / 2048;
+            metrics->fBottom = -size * kElegantBottom / 2048;
+            metrics->fAscent = -size * kElegantAscent / 2048;
+            metrics->fDescent = -size * kElegantDescent / 2048;
+            metrics->fLeading = size * kElegantLeading / 2048;
+            spacing = metrics->fDescent - metrics->fAscent + metrics->fLeading;
+        }
+        return spacing;
+    }
+
+    static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle,
+            jlong typefaceHandle, jobject metricsObj) {
+        Paint::FontMetrics metrics;
+        SkScalar spacing = getMetricsInternal(paintHandle, typefaceHandle, &metrics);
+
+        if (metricsObj) {
+            SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class));
+            env->SetFloatField(metricsObj, gFontMetrics_fieldID.top, SkScalarToFloat(metrics.fTop));
+            env->SetFloatField(metricsObj, gFontMetrics_fieldID.ascent, SkScalarToFloat(metrics.fAscent));
+            env->SetFloatField(metricsObj, gFontMetrics_fieldID.descent, SkScalarToFloat(metrics.fDescent));
+            env->SetFloatField(metricsObj, gFontMetrics_fieldID.bottom, SkScalarToFloat(metrics.fBottom));
+            env->SetFloatField(metricsObj, gFontMetrics_fieldID.leading, SkScalarToFloat(metrics.fLeading));
+        }
+        return SkScalarToFloat(spacing);
+    }
+
+    static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle,
+            jlong typefaceHandle, jobject metricsObj) {
+        Paint::FontMetrics metrics;
+
+        getMetricsInternal(paintHandle, typefaceHandle, &metrics);
+        int ascent = SkScalarRoundToInt(metrics.fAscent);
+        int descent = SkScalarRoundToInt(metrics.fDescent);
+        int leading = SkScalarRoundToInt(metrics.fLeading);
+
+        if (metricsObj) {
+            SkASSERT(env->IsInstanceOf(metricsObj, gFontMetricsInt_class));
+            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.top, SkScalarFloorToInt(metrics.fTop));
+            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.ascent, ascent);
+            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.descent, descent);
+            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.bottom, SkScalarCeilToInt(metrics.fBottom));
+            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.leading, leading);
+        }
+        return descent - ascent + leading;
+    }
+
+
+    // ------------------ @CriticalNative ---------------------------
+
+    static void reset(jlong objHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        obj->reset();
+        defaultSettingsForAndroid(obj);
+    }
+
+    static void assign(jlong dstPaintHandle, jlong srcPaintHandle) {
+        Paint* dst = reinterpret_cast<Paint*>(dstPaintHandle);
+        const Paint* src = reinterpret_cast<Paint*>(srcPaintHandle);
+        *dst = *src;
+    }
+
+    // Equivalent to the Java Paint's FILTER_BITMAP_FLAG.
+    static const uint32_t sFilterBitmapFlag = 0x02;
+
+    static jint getFlags(jlong paintHandle) {
+        Paint* nativePaint = reinterpret_cast<Paint*>(paintHandle);
+        uint32_t result = nativePaint->getFlags();
+        result &= ~sFilterBitmapFlag; // Filtering no longer stored in this bit. Mask away.
+        if (nativePaint->getFilterQuality() != kNone_SkFilterQuality) {
+            result |= sFilterBitmapFlag;
+        }
+        return static_cast<jint>(result);
+    }
+
+    static void setFlags(jlong paintHandle, jint flags) {
+        Paint* nativePaint = reinterpret_cast<Paint*>(paintHandle);
+        // Instead of modifying 0x02, change the filter level.
+        nativePaint->setFilterQuality(flags & sFilterBitmapFlag
+                ? kLow_SkFilterQuality
+                : kNone_SkFilterQuality);
+        // Don't pass through filter flag, which is no longer stored in paint's flags.
+        flags &= ~sFilterBitmapFlag;
+        // Use the existing value for 0x02.
+        const uint32_t existing0x02Flag = nativePaint->getFlags() & sFilterBitmapFlag;
+        flags |= existing0x02Flag;
+        nativePaint->setFlags(flags);
+    }
+
+    static jint getHinting(jlong paintHandle) {
+        return reinterpret_cast<Paint*>(paintHandle)->getHinting()
+                == Paint::kNo_Hinting ? 0 : 1;
+    }
+
+    static void setHinting(jlong paintHandle, jint mode) {
+        reinterpret_cast<Paint*>(paintHandle)->setHinting(
+                mode == 0 ? Paint::kNo_Hinting : Paint::kNormal_Hinting);
+    }
+
+    static void setAntiAlias(jlong paintHandle, jboolean aa) {
+        reinterpret_cast<Paint*>(paintHandle)->setAntiAlias(aa);
+    }
+
+    static void setLinearText(jlong paintHandle, jboolean linearText) {
+        reinterpret_cast<Paint*>(paintHandle)->setLinearText(linearText);
+    }
+
+    static void setSubpixelText(jlong paintHandle, jboolean subpixelText) {
+        reinterpret_cast<Paint*>(paintHandle)->setSubpixelText(subpixelText);
+    }
+
+    static void setUnderlineText(jlong paintHandle, jboolean underlineText) {
+        reinterpret_cast<Paint*>(paintHandle)->setUnderlineText(underlineText);
+    }
+
+    static void setStrikeThruText(jlong paintHandle, jboolean strikeThruText) {
+        reinterpret_cast<Paint*>(paintHandle)->setStrikeThruText(strikeThruText);
+    }
+
+    static void setFakeBoldText(jlong paintHandle, jboolean fakeBoldText) {
+        reinterpret_cast<Paint*>(paintHandle)->setFakeBoldText(fakeBoldText);
+    }
+
+    static void setFilterBitmap(jlong paintHandle, jboolean filterBitmap) {
+        reinterpret_cast<Paint*>(paintHandle)->setFilterQuality(
+                filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality);
+    }
+
+    static void setDither(jlong paintHandle, jboolean dither) {
+        reinterpret_cast<Paint*>(paintHandle)->setDither(dither);
+    }
+
+    static jint getStyle(jlong objHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        return static_cast<jint>(obj->getStyle());
+    }
+
+    static void setStyle(jlong objHandle, jint styleHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        Paint::Style style = static_cast<Paint::Style>(styleHandle);
+        obj->setStyle(style);
+    }
+
+    static jint getColor(jlong paintHandle) {
+        int color;
+        color = reinterpret_cast<Paint*>(paintHandle)->getColor();
+        return static_cast<jint>(color);
+    }
+
+    static jint getAlpha(jlong paintHandle) {
+        int alpha;
+        alpha = reinterpret_cast<Paint*>(paintHandle)->getAlpha();
+        return static_cast<jint>(alpha);
+    }
+
+    static void setColor(jlong paintHandle, jint color) {
+        reinterpret_cast<Paint*>(paintHandle)->setColor(color);
+    }
+
+    static void setAlpha(jlong paintHandle, jint a) {
+        reinterpret_cast<Paint*>(paintHandle)->setAlpha(a);
+    }
+
+    static jfloat getStrokeWidth(jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeWidth());
+    }
+
+    static void setStrokeWidth(jlong paintHandle, jfloat width) {
+        reinterpret_cast<Paint*>(paintHandle)->setStrokeWidth(width);
+    }
+
+    static jfloat getStrokeMiter(jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeMiter());
+    }
+
+    static void setStrokeMiter(jlong paintHandle, jfloat miter) {
+        reinterpret_cast<Paint*>(paintHandle)->setStrokeMiter(miter);
+    }
+
+    static jint getStrokeCap(jlong objHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        return static_cast<jint>(obj->getStrokeCap());
+    }
+
+    static void setStrokeCap(jlong objHandle, jint capHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        Paint::Cap cap = static_cast<Paint::Cap>(capHandle);
+        obj->setStrokeCap(cap);
+    }
+
+    static jint getStrokeJoin(jlong objHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        return static_cast<jint>(obj->getStrokeJoin());
+    }
+
+    static void setStrokeJoin(jlong objHandle, jint joinHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        Paint::Join join = (Paint::Join) joinHandle;
+        obj->setStrokeJoin(join);
+    }
+
+    static jboolean getFillPath(jlong objHandle, jlong srcHandle, jlong dstHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
+        SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
+        return obj->getFillPath(*src, dst) ? JNI_TRUE : JNI_FALSE;
+    }
+
+    static jlong setShader(jlong objHandle, jlong shaderHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
+        return reinterpret_cast<jlong>(obj->setShader(shader));
+    }
+
+    static jlong setColorFilter(jlong objHandle, jlong filterHandle) {
+        Paint* obj = reinterpret_cast<Paint *>(objHandle);
+        SkColorFilter* filter  = reinterpret_cast<SkColorFilter *>(filterHandle);
+        return reinterpret_cast<jlong>(obj->setColorFilter(filter));
+    }
+
+    static void setXfermode(jlong paintHandle, jint xfermodeHandle) {
+        // validate that the Java enum values match our expectations
+        static_assert(0 == SkXfermode::kClear_Mode, "xfermode_mismatch");
+        static_assert(1 == SkXfermode::kSrc_Mode, "xfermode_mismatch");
+        static_assert(2 == SkXfermode::kDst_Mode, "xfermode_mismatch");
+        static_assert(3 == SkXfermode::kSrcOver_Mode, "xfermode_mismatch");
+        static_assert(4 == SkXfermode::kDstOver_Mode, "xfermode_mismatch");
+        static_assert(5 == SkXfermode::kSrcIn_Mode, "xfermode_mismatch");
+        static_assert(6 == SkXfermode::kDstIn_Mode, "xfermode_mismatch");
+        static_assert(7 == SkXfermode::kSrcOut_Mode, "xfermode_mismatch");
+        static_assert(8 == SkXfermode::kDstOut_Mode, "xfermode_mismatch");
+        static_assert(9 == SkXfermode::kSrcATop_Mode, "xfermode_mismatch");
+        static_assert(10 == SkXfermode::kDstATop_Mode, "xfermode_mismatch");
+        static_assert(11 == SkXfermode::kXor_Mode, "xfermode_mismatch");
+        static_assert(16 == SkXfermode::kDarken_Mode, "xfermode_mismatch");
+        static_assert(17 == SkXfermode::kLighten_Mode, "xfermode_mismatch");
+        static_assert(13 == SkXfermode::kModulate_Mode, "xfermode_mismatch");
+        static_assert(14 == SkXfermode::kScreen_Mode, "xfermode_mismatch");
+        static_assert(12 == SkXfermode::kPlus_Mode, "xfermode_mismatch");
+        static_assert(15 == SkXfermode::kOverlay_Mode, "xfermode_mismatch");
+
+        SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(xfermodeHandle);
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        paint->setXfermodeMode(mode);
+    }
+
+    static jlong setPathEffect(jlong objHandle, jlong effectHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        SkPathEffect* effect  = reinterpret_cast<SkPathEffect*>(effectHandle);
+        return reinterpret_cast<jlong>(obj->setPathEffect(effect));
+    }
+
+    static jlong setMaskFilter(jlong objHandle, jlong maskfilterHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        SkMaskFilter* maskfilter  = reinterpret_cast<SkMaskFilter*>(maskfilterHandle);
+        return reinterpret_cast<jlong>(obj->setMaskFilter(maskfilter));
+    }
+
+    static jlong setTypeface(jlong objHandle, jlong typefaceHandle) {
+        // TODO: in Paint refactoring, set typeface on android Paint, not Paint
+        return 0;
+    }
+
+    static jlong setRasterizer(jlong objHandle, jlong rasterizerHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        SkAutoTUnref<SkRasterizer> rasterizer(GraphicsJNI::refNativeRasterizer(rasterizerHandle));
+        return reinterpret_cast<jlong>(obj->setRasterizer(rasterizer));
+    }
+
+    static jint getTextAlign(jlong objHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        return static_cast<jint>(obj->getTextAlign());
+    }
+
+    static void setTextAlign(jlong objHandle, jint alignHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        Paint::Align align = static_cast<Paint::Align>(alignHandle);
+        obj->setTextAlign(align);
+    }
+
+    static void setTextLocalesByMinikinLangListId(jlong objHandle,
+            jint minikinLangListId) {
+        Paint* obj = reinterpret_cast<Paint*>(objHandle);
+        obj->setMinikinLangListId(minikinLangListId);
+    }
+
+    static jboolean isElegantTextHeight(jlong paintHandle) {
+        Paint* obj = reinterpret_cast<Paint*>(paintHandle);
+        return obj->getFontVariant() == minikin::VARIANT_ELEGANT;
+    }
+
+    static void setElegantTextHeight(jlong paintHandle, jboolean aa) {
+        Paint* obj = reinterpret_cast<Paint*>(paintHandle);
+        obj->setFontVariant(aa ? minikin::VARIANT_ELEGANT : minikin::VARIANT_DEFAULT);
+    }
+
+    static jfloat getTextSize(jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextSize());
+    }
+
+    static void setTextSize(jlong paintHandle, jfloat textSize) {
+        reinterpret_cast<Paint*>(paintHandle)->setTextSize(textSize);
+    }
+
+    static jfloat getTextScaleX(jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextScaleX());
+    }
+
+    static void setTextScaleX(jlong paintHandle, jfloat scaleX) {
+        reinterpret_cast<Paint*>(paintHandle)->setTextScaleX(scaleX);
+    }
+
+    static jfloat getTextSkewX(jlong paintHandle) {
+        return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getTextSkewX());
+    }
+
+    static void setTextSkewX(jlong paintHandle, jfloat skewX) {
+        reinterpret_cast<Paint*>(paintHandle)->setTextSkewX(skewX);
+    }
+
+    static jfloat getLetterSpacing(jlong paintHandle) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        return paint->getLetterSpacing();
+    }
+
+    static void setLetterSpacing(jlong paintHandle, jfloat letterSpacing) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        paint->setLetterSpacing(letterSpacing);
+    }
+
+    static jint getHyphenEdit(jlong paintHandle, jint hyphen) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        return paint->getHyphenEdit();
+    }
+
+    static void setHyphenEdit(jlong paintHandle, jint hyphen) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        paint->setHyphenEdit((uint32_t)hyphen);
+    }
+
+    static jfloat ascent(jlong paintHandle, jlong typefaceHandle) {
+        Paint::FontMetrics metrics;
+        getMetricsInternal(paintHandle, typefaceHandle, &metrics);
+        return SkScalarToFloat(metrics.fAscent);
+    }
+
+    static jfloat descent(jlong paintHandle, jlong typefaceHandle) {
+        Paint::FontMetrics metrics;
+        getMetricsInternal(paintHandle, typefaceHandle, &metrics);
+        return SkScalarToFloat(metrics.fDescent);
+    }
+
+    static void setShadowLayer(jlong paintHandle, jfloat radius,
+                               jfloat dx, jfloat dy, jint color) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        if (radius <= 0) {
+            paint->setLooper(nullptr);
+        }
+        else {
+            SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
+            paint->setLooper(SkBlurDrawLooper::Create((SkColor)color, sigma, dx, dy))->unref();
+        }
+    }
+
+    static jboolean hasShadowLayer(jlong paintHandle) {
+        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+        return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr);
+    }
+
 }; // namespace PaintGlue
 
 static const JNINativeMethod methods[] = {
     {"nGetNativeFinalizer", "()J", (void*) PaintGlue::getNativeFinalizer},
     {"nInit","()J", (void*) PaintGlue::init},
     {"nInitWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
-
-    {"nReset","!(J)V", (void*) PaintGlue::reset},
-    {"nSet","!(JJ)V", (void*) PaintGlue::assign},
-    {"nGetFlags","!(J)I", (void*) PaintGlue::getFlags},
-    {"nSetFlags","!(JI)V", (void*) PaintGlue::setFlags},
-    {"nGetHinting","!(J)I", (void*) PaintGlue::getHinting},
-    {"nSetHinting","!(JI)V", (void*) PaintGlue::setHinting},
-    {"nSetAntiAlias","!(JZ)V", (void*) PaintGlue::setAntiAlias},
-    {"nSetSubpixelText","!(JZ)V", (void*) PaintGlue::setSubpixelText},
-    {"nSetLinearText","!(JZ)V", (void*) PaintGlue::setLinearText},
-    {"nSetUnderlineText","!(JZ)V", (void*) PaintGlue::setUnderlineText},
-    {"nSetStrikeThruText","!(JZ)V", (void*) PaintGlue::setStrikeThruText},
-    {"nSetFakeBoldText","!(JZ)V", (void*) PaintGlue::setFakeBoldText},
-    {"nSetFilterBitmap","!(JZ)V", (void*) PaintGlue::setFilterBitmap},
-    {"nSetDither","!(JZ)V", (void*) PaintGlue::setDither},
-    {"nGetStyle","!(J)I", (void*) PaintGlue::getStyle},
-    {"nSetStyle","!(JI)V", (void*) PaintGlue::setStyle},
-    {"nGetColor","!(J)I", (void*) PaintGlue::getColor},
-    {"nSetColor","!(JI)V", (void*) PaintGlue::setColor},
-    {"nGetAlpha","!(J)I", (void*) PaintGlue::getAlpha},
-    {"nSetAlpha","!(JI)V", (void*) PaintGlue::setAlpha},
-    {"nGetStrokeWidth","!(J)F", (void*) PaintGlue::getStrokeWidth},
-    {"nSetStrokeWidth","!(JF)V", (void*) PaintGlue::setStrokeWidth},
-    {"nGetStrokeMiter","!(J)F", (void*) PaintGlue::getStrokeMiter},
-    {"nSetStrokeMiter","!(JF)V", (void*) PaintGlue::setStrokeMiter},
-    {"nGetStrokeCap","!(J)I", (void*) PaintGlue::getStrokeCap},
-    {"nSetStrokeCap","!(JI)V", (void*) PaintGlue::setStrokeCap},
-    {"nGetStrokeJoin","!(J)I", (void*) PaintGlue::getStrokeJoin},
-    {"nSetStrokeJoin","!(JI)V", (void*) PaintGlue::setStrokeJoin},
-    {"nGetFillPath","!(JJJ)Z", (void*) PaintGlue::getFillPath},
-    {"nSetShader","!(JJ)J", (void*) PaintGlue::setShader},
-    {"nSetColorFilter","!(JJ)J", (void*) PaintGlue::setColorFilter},
-    {"nSetXfermode","!(JI)V", (void*) PaintGlue::setXfermode},
-    {"nSetPathEffect","!(JJ)J", (void*) PaintGlue::setPathEffect},
-    {"nSetMaskFilter","!(JJ)J", (void*) PaintGlue::setMaskFilter},
-    {"nSetTypeface","!(JJ)J", (void*) PaintGlue::setTypeface},
-    {"nSetRasterizer","!(JJ)J", (void*) PaintGlue::setRasterizer},
-    {"nGetTextAlign","!(J)I", (void*) PaintGlue::getTextAlign},
-    {"nSetTextAlign","!(JI)V", (void*) PaintGlue::setTextAlign},
-    {"nSetTextLocales","!(JLjava/lang/String;)I", (void*) PaintGlue::setTextLocales},
-    {"nSetTextLocalesByMinikinLangListId","!(JI)V",
-            (void*) PaintGlue::setTextLocalesByMinikinLangListId},
-    {"nIsElegantTextHeight","!(J)Z", (void*) PaintGlue::isElegantTextHeight},
-    {"nSetElegantTextHeight","!(JZ)V", (void*) PaintGlue::setElegantTextHeight},
-    {"nGetTextSize","!(J)F", (void*) PaintGlue::getTextSize},
-    {"nSetTextSize","!(JF)V", (void*) PaintGlue::setTextSize},
-    {"nGetTextScaleX","!(J)F", (void*) PaintGlue::getTextScaleX},
-    {"nSetTextScaleX","!(JF)V", (void*) PaintGlue::setTextScaleX},
-    {"nGetTextSkewX","!(J)F", (void*) PaintGlue::getTextSkewX},
-    {"nSetTextSkewX","!(JF)V", (void*) PaintGlue::setTextSkewX},
-    {"nGetLetterSpacing","!(J)F", (void*) PaintGlue::getLetterSpacing},
-    {"nSetLetterSpacing","!(JF)V", (void*) PaintGlue::setLetterSpacing},
-    {"nSetFontFeatureSettings","(JLjava/lang/String;)V",
-            (void*) PaintGlue::setFontFeatureSettings},
-    {"nGetHyphenEdit", "!(J)I", (void*) PaintGlue::getHyphenEdit},
-    {"nSetHyphenEdit", "!(JI)V", (void*) PaintGlue::setHyphenEdit},
-    {"nAscent","!(JJ)F", (void*) PaintGlue::ascent},
-    {"nDescent","!(JJ)F", (void*) PaintGlue::descent},
-
-    {"nGetFontMetrics", "!(JJLandroid/graphics/Paint$FontMetrics;)F",
-            (void*)PaintGlue::getFontMetrics},
-    {"nGetFontMetricsInt", "!(JJLandroid/graphics/Paint$FontMetricsInt;)I",
-            (void*)PaintGlue::getFontMetricsInt},
-
     {"nBreakText","(JJ[CIIFI[F)I", (void*) PaintGlue::breakTextC},
     {"nBreakText","(JJLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS},
     {"nGetTextAdvances","(JJ[CIIIII[FI)F",
@@ -1034,8 +976,74 @@
     {"nGetOffsetForAdvance", "(JJ[CIIIIZF)I",
             (void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I},
 
-    {"nSetShadowLayer", "!(JFFFI)V", (void*)PaintGlue::setShadowLayer},
-    {"nHasShadowLayer", "!(J)Z", (void*)PaintGlue::hasShadowLayer}
+    // --------------- @FastNative ----------------------
+
+    {"nSetTextLocales","(JLjava/lang/String;)I", (void*) PaintGlue::setTextLocales},
+    {"nSetFontFeatureSettings","(JLjava/lang/String;)V",
+                (void*) PaintGlue::setFontFeatureSettings},
+    {"nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F",
+                (void*)PaintGlue::getFontMetrics},
+    {"nGetFontMetricsInt", "(JJLandroid/graphics/Paint$FontMetricsInt;)I",
+            (void*)PaintGlue::getFontMetricsInt},
+
+    // --------------- @CriticalNative ------------------
+
+    {"nReset","(J)V", (void*) PaintGlue::reset},
+    {"nSet","(JJ)V", (void*) PaintGlue::assign},
+    {"nGetFlags","(J)I", (void*) PaintGlue::getFlags},
+    {"nSetFlags","(JI)V", (void*) PaintGlue::setFlags},
+    {"nGetHinting","(J)I", (void*) PaintGlue::getHinting},
+    {"nSetHinting","(JI)V", (void*) PaintGlue::setHinting},
+    {"nSetAntiAlias","(JZ)V", (void*) PaintGlue::setAntiAlias},
+    {"nSetSubpixelText","(JZ)V", (void*) PaintGlue::setSubpixelText},
+    {"nSetLinearText","(JZ)V", (void*) PaintGlue::setLinearText},
+    {"nSetUnderlineText","(JZ)V", (void*) PaintGlue::setUnderlineText},
+    {"nSetStrikeThruText","(JZ)V", (void*) PaintGlue::setStrikeThruText},
+    {"nSetFakeBoldText","(JZ)V", (void*) PaintGlue::setFakeBoldText},
+    {"nSetFilterBitmap","(JZ)V", (void*) PaintGlue::setFilterBitmap},
+    {"nSetDither","(JZ)V", (void*) PaintGlue::setDither},
+    {"nGetStyle","(J)I", (void*) PaintGlue::getStyle},
+    {"nSetStyle","(JI)V", (void*) PaintGlue::setStyle},
+    {"nGetColor","(J)I", (void*) PaintGlue::getColor},
+    {"nSetColor","(JI)V", (void*) PaintGlue::setColor},
+    {"nGetAlpha","(J)I", (void*) PaintGlue::getAlpha},
+    {"nSetAlpha","(JI)V", (void*) PaintGlue::setAlpha},
+    {"nGetStrokeWidth","(J)F", (void*) PaintGlue::getStrokeWidth},
+    {"nSetStrokeWidth","(JF)V", (void*) PaintGlue::setStrokeWidth},
+    {"nGetStrokeMiter","(J)F", (void*) PaintGlue::getStrokeMiter},
+    {"nSetStrokeMiter","(JF)V", (void*) PaintGlue::setStrokeMiter},
+    {"nGetStrokeCap","(J)I", (void*) PaintGlue::getStrokeCap},
+    {"nSetStrokeCap","(JI)V", (void*) PaintGlue::setStrokeCap},
+    {"nGetStrokeJoin","(J)I", (void*) PaintGlue::getStrokeJoin},
+    {"nSetStrokeJoin","(JI)V", (void*) PaintGlue::setStrokeJoin},
+    {"nGetFillPath","(JJJ)Z", (void*) PaintGlue::getFillPath},
+    {"nSetShader","(JJ)J", (void*) PaintGlue::setShader},
+    {"nSetColorFilter","(JJ)J", (void*) PaintGlue::setColorFilter},
+    {"nSetXfermode","(JI)V", (void*) PaintGlue::setXfermode},
+    {"nSetPathEffect","(JJ)J", (void*) PaintGlue::setPathEffect},
+    {"nSetMaskFilter","(JJ)J", (void*) PaintGlue::setMaskFilter},
+    {"nSetTypeface","(JJ)J", (void*) PaintGlue::setTypeface},
+    {"nSetRasterizer","(JJ)J", (void*) PaintGlue::setRasterizer},
+    {"nGetTextAlign","(J)I", (void*) PaintGlue::getTextAlign},
+    {"nSetTextAlign","(JI)V", (void*) PaintGlue::setTextAlign},
+    {"nSetTextLocalesByMinikinLangListId","(JI)V",
+            (void*) PaintGlue::setTextLocalesByMinikinLangListId},
+    {"nIsElegantTextHeight","(J)Z", (void*) PaintGlue::isElegantTextHeight},
+    {"nSetElegantTextHeight","(JZ)V", (void*) PaintGlue::setElegantTextHeight},
+    {"nGetTextSize","(J)F", (void*) PaintGlue::getTextSize},
+    {"nSetTextSize","(JF)V", (void*) PaintGlue::setTextSize},
+    {"nGetTextScaleX","(J)F", (void*) PaintGlue::getTextScaleX},
+    {"nSetTextScaleX","(JF)V", (void*) PaintGlue::setTextScaleX},
+    {"nGetTextSkewX","(J)F", (void*) PaintGlue::getTextSkewX},
+    {"nSetTextSkewX","(JF)V", (void*) PaintGlue::setTextSkewX},
+    {"nGetLetterSpacing","(J)F", (void*) PaintGlue::getLetterSpacing},
+    {"nSetLetterSpacing","(JF)V", (void*) PaintGlue::setLetterSpacing},
+    {"nGetHyphenEdit", "(J)I", (void*) PaintGlue::getHyphenEdit},
+    {"nSetHyphenEdit", "(JI)V", (void*) PaintGlue::setHyphenEdit},
+    {"nAscent","(JJ)F", (void*) PaintGlue::ascent},
+    {"nDescent","(JJ)F", (void*) PaintGlue::descent},
+    {"nSetShadowLayer", "(JFFFI)V", (void*)PaintGlue::setShadowLayer},
+    {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer}
 };
 
 int register_android_graphics_Paint(JNIEnv* env) {
diff --git a/core/jni/android_hardware_location_ContextHubService.cpp b/core/jni/android_hardware_location_ContextHubService.cpp
index 8eb39e1..fbccfd5 100644
--- a/core/jni/android_hardware_location_ContextHubService.cpp
+++ b/core/jni/android_hardware_location_ContextHubService.cpp
@@ -33,6 +33,7 @@
 #include <unordered_map>
 #include <queue>
 
+#include <android-base/macros.h>
 #include <cutils/log.h>
 
 #include "JNIHelp.h"
@@ -704,10 +705,10 @@
     }
 
     jbyteArray jmsg = env->NewByteArray(msgLen);
-    jintArray jheader = env->NewIntArray(sizeof(header));
+    jintArray jheader = env->NewIntArray(arraysize(header));
 
     env->SetByteArrayRegion(jmsg, 0, msgLen, (jbyte *)msg);
-    env->SetIntArrayRegion(jheader, 0, sizeof(header), (jint *)header);
+    env->SetIntArrayRegion(jheader, 0, arraysize(header), (jint *)header);
 
     ALOGI("Passing msg type %" PRIu32 " from app %" PRIu32 " from hub %" PRIu32,
           header[HEADER_FIELD_MSG_TYPE], header[HEADER_FIELD_APP_INSTANCE],
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index e0bfecb..97c7f04 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -839,7 +839,8 @@
     size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
 extern "C" void free_malloc_leak_info(uint8_t* info);
 #define SIZE_FLAG_ZYGOTE_CHILD  (1<<31)
-#define BACKTRACE_SIZE          32
+
+static size_t gNumBacktraceElements;
 
 /*
  * This is a qsort() callback.
@@ -859,11 +860,11 @@
         return -1;
     }
 
-    intptr_t* bt1 = (intptr_t*)(rec1 + 2);
-    intptr_t* bt2 = (intptr_t*)(rec2 + 2);
-    for (size_t idx = 0; idx < BACKTRACE_SIZE; idx++) {
-        intptr_t addr1 = bt1[idx];
-        intptr_t addr2 = bt2[idx];
+    uintptr_t* bt1 = (uintptr_t*)(rec1 + 2);
+    uintptr_t* bt2 = (uintptr_t*)(rec2 + 2);
+    for (size_t idx = 0; idx < gNumBacktraceElements; idx++) {
+        uintptr_t addr1 = bt1[idx];
+        uintptr_t addr2 = bt2[idx];
         if (addr1 == addr2) {
             if (addr1 == 0)
                 break;
@@ -907,9 +908,10 @@
     if (info == NULL) {
         fprintf(fp, "Native heap dump not available. To enable, run these"
                     " commands (requires root):\n");
-        fprintf(fp, "$ adb shell setprop libc.debug.malloc 1\n");
-        fprintf(fp, "$ adb shell stop\n");
-        fprintf(fp, "$ adb shell start\n");
+        fprintf(fp, "# adb shell stop\n");
+        fprintf(fp, "# adb shell setprop libc.debug.malloc.options "
+                    "backtrace\n");
+        fprintf(fp, "# adb shell start\n");
         return;
     }
     assert(infoSize != 0);
@@ -920,13 +922,11 @@
     size_t recordCount = overallSize / infoSize;
     fprintf(fp, "Total memory: %zu\n", totalMemory);
     fprintf(fp, "Allocation records: %zd\n", recordCount);
-    if (backtraceSize != BACKTRACE_SIZE) {
-        fprintf(fp, "WARNING: mismatched backtrace sizes (%zu vs. %d)\n",
-            backtraceSize, BACKTRACE_SIZE);
-    }
+    fprintf(fp, "Backtrace size: %zd\n", backtraceSize);
     fprintf(fp, "\n");
 
     /* re-sort the entries */
+    gNumBacktraceElements = backtraceSize;
     qsort(info, recordCount, infoSize, compareHeapRecords);
 
     /* dump the entries to the file */
@@ -934,7 +934,7 @@
     for (size_t idx = 0; idx < recordCount; idx++) {
         size_t size = *(size_t*) ptr;
         size_t allocations = *(size_t*) (ptr + sizeof(size_t));
-        intptr_t* backtrace = (intptr_t*) (ptr + sizeof(size_t) * 2);
+        uintptr_t* backtrace = (uintptr_t*) (ptr + sizeof(size_t) * 2);
 
         fprintf(fp, "z %d  sz %8zu  num %4zu  bt",
                 (size & SIZE_FLAG_ZYGOTE_CHILD) != 0,
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index 0a8ae2b..8f7908a 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -722,33 +722,50 @@
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gParcelMethods[] = {
-    {"nativeDataSize",            "!(J)I", (void*)android_os_Parcel_dataSize},
-    {"nativeDataAvail",           "!(J)I", (void*)android_os_Parcel_dataAvail},
-    {"nativeDataPosition",        "!(J)I", (void*)android_os_Parcel_dataPosition},
-    {"nativeDataCapacity",        "!(J)I", (void*)android_os_Parcel_dataCapacity},
-    {"nativeSetDataSize",         "!(JI)J", (void*)android_os_Parcel_setDataSize},
-    {"nativeSetDataPosition",     "!(JI)V", (void*)android_os_Parcel_setDataPosition},
-    {"nativeSetDataCapacity",     "!(JI)V", (void*)android_os_Parcel_setDataCapacity},
+    // @FastNative
+    {"nativeDataSize",            "(J)I", (void*)android_os_Parcel_dataSize},
+    // @FastNative
+    {"nativeDataAvail",           "(J)I", (void*)android_os_Parcel_dataAvail},
+    // @FastNative
+    {"nativeDataPosition",        "(J)I", (void*)android_os_Parcel_dataPosition},
+    // @FastNative
+    {"nativeDataCapacity",        "(J)I", (void*)android_os_Parcel_dataCapacity},
+    // @FastNative
+    {"nativeSetDataSize",         "(JI)J", (void*)android_os_Parcel_setDataSize},
+    // @FastNative
+    {"nativeSetDataPosition",     "(JI)V", (void*)android_os_Parcel_setDataPosition},
+    // @FastNative
+    {"nativeSetDataCapacity",     "(JI)V", (void*)android_os_Parcel_setDataCapacity},
 
-    {"nativePushAllowFds",        "!(JZ)Z", (void*)android_os_Parcel_pushAllowFds},
-    {"nativeRestoreAllowFds",     "!(JZ)V", (void*)android_os_Parcel_restoreAllowFds},
+    // @FastNative
+    {"nativePushAllowFds",        "(JZ)Z", (void*)android_os_Parcel_pushAllowFds},
+    // @FastNative
+    {"nativeRestoreAllowFds",     "(JZ)V", (void*)android_os_Parcel_restoreAllowFds},
 
     {"nativeWriteByteArray",      "(J[BII)V", (void*)android_os_Parcel_writeNative},
     {"nativeWriteBlob",           "(J[BII)V", (void*)android_os_Parcel_writeBlob},
-    {"nativeWriteInt",            "!(JI)V", (void*)android_os_Parcel_writeInt},
-    {"nativeWriteLong",           "!(JJ)V", (void*)android_os_Parcel_writeLong},
-    {"nativeWriteFloat",          "!(JF)V", (void*)android_os_Parcel_writeFloat},
-    {"nativeWriteDouble",         "!(JD)V", (void*)android_os_Parcel_writeDouble},
+    // @FastNative
+    {"nativeWriteInt",            "(JI)V", (void*)android_os_Parcel_writeInt},
+    // @FastNative
+    {"nativeWriteLong",           "(JJ)V", (void*)android_os_Parcel_writeLong},
+    // @FastNative
+    {"nativeWriteFloat",          "(JF)V", (void*)android_os_Parcel_writeFloat},
+    // @FastNative
+    {"nativeWriteDouble",         "(JD)V", (void*)android_os_Parcel_writeDouble},
     {"nativeWriteString",         "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeString},
     {"nativeWriteStrongBinder",   "(JLandroid/os/IBinder;)V", (void*)android_os_Parcel_writeStrongBinder},
     {"nativeWriteFileDescriptor", "(JLjava/io/FileDescriptor;)J", (void*)android_os_Parcel_writeFileDescriptor},
 
     {"nativeCreateByteArray",     "(J)[B", (void*)android_os_Parcel_createByteArray},
     {"nativeReadBlob",            "(J)[B", (void*)android_os_Parcel_readBlob},
-    {"nativeReadInt",             "!(J)I", (void*)android_os_Parcel_readInt},
-    {"nativeReadLong",            "!(J)J", (void*)android_os_Parcel_readLong},
-    {"nativeReadFloat",           "!(J)F", (void*)android_os_Parcel_readFloat},
-    {"nativeReadDouble",          "!(J)D", (void*)android_os_Parcel_readDouble},
+    // @FastNative
+    {"nativeReadInt",             "(J)I", (void*)android_os_Parcel_readInt},
+    // @FastNative
+    {"nativeReadLong",            "(J)J", (void*)android_os_Parcel_readLong},
+    // @FastNative
+    {"nativeReadFloat",           "(J)F", (void*)android_os_Parcel_readFloat},
+    // @FastNative
+    {"nativeReadDouble",          "(J)D", (void*)android_os_Parcel_readDouble},
     {"nativeReadString",          "(J)Ljava/lang/String;", (void*)android_os_Parcel_readString},
     {"nativeReadStrongBinder",    "(J)Landroid/os/IBinder;", (void*)android_os_Parcel_readStrongBinder},
     {"nativeReadFileDescriptor",  "(J)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_readFileDescriptor},
@@ -765,7 +782,8 @@
     {"nativeMarshall",            "(J)[B", (void*)android_os_Parcel_marshall},
     {"nativeUnmarshall",          "(J[BII)J", (void*)android_os_Parcel_unmarshall},
     {"nativeAppendFrom",          "(JJII)J", (void*)android_os_Parcel_appendFrom},
-    {"nativeHasFileDescriptors",  "!(J)Z", (void*)android_os_Parcel_hasFileDescriptors},
+    // @FastNative
+    {"nativeHasFileDescriptors",  "(J)Z", (void*)android_os_Parcel_hasFileDescriptors},
     {"nativeWriteInterfaceToken", "(JLjava/lang/String;)V", (void*)android_os_Parcel_writeInterfaceToken},
     {"nativeEnforceInterface",    "(JLjava/lang/String;)V", (void*)android_os_Parcel_enforceInterface},
 
diff --git a/core/jni/android_os_SystemClock.cpp b/core/jni/android_os_SystemClock.cpp
index ccb833a..2fade69 100644
--- a/core/jni/android_os_SystemClock.cpp
+++ b/core/jni/android_os_SystemClock.cpp
@@ -36,29 +36,18 @@
 
 namespace android {
 
-/*
- * native public static long uptimeMillis();
- */
-static jlong android_os_SystemClock_uptimeMillis(JNIEnv* env,
-        jobject clazz)
-{
-    return (jlong)uptimeMillis();
-}
-
-/*
- * native public static long elapsedRealtime();
- */
-static jlong android_os_SystemClock_elapsedRealtime(JNIEnv* env,
-        jobject clazz)
-{
-    return (jlong)elapsedRealtime();
-}
+static_assert(std::is_same<int64_t, jlong>::value, "jlong isn't an int64_t");
+static_assert(std::is_same<decltype(uptimeMillis()), int64_t>::value,
+        "uptimeMillis signature change, expected int64_t return value");
+static_assert(std::is_same<decltype(elapsedRealtime()), int64_t>::value,
+        "uptimeMillis signature change, expected int64_t return value");
+static_assert(std::is_same<decltype(elapsedRealtimeNano()), int64_t>::value,
+        "uptimeMillis signature change, expected int64_t return value");
 
 /*
  * native public static long currentThreadTimeMillis();
  */
-static jlong android_os_SystemClock_currentThreadTimeMillis(JNIEnv* env,
-        jobject clazz)
+static jlong android_os_SystemClock_currentThreadTimeMillis()
 {
     struct timespec tm;
 
@@ -70,8 +59,7 @@
 /*
  * native public static long currentThreadTimeMicro();
  */
-static jlong android_os_SystemClock_currentThreadTimeMicro(JNIEnv* env,
-        jobject clazz)
+static jlong android_os_SystemClock_currentThreadTimeMicro()
 {
     struct timespec tm;
 
@@ -83,8 +71,7 @@
 /*
  * native public static long currentTimeMicro();
  */
-static jlong android_os_SystemClock_currentTimeMicro(JNIEnv* env,
-        jobject clazz)
+static jlong android_os_SystemClock_currentTimeMicro()
 {
     struct timeval tv;
 
@@ -93,31 +80,22 @@
 }
 
 /*
- * public static native long elapsedRealtimeNano();
- */
-static jlong android_os_SystemClock_elapsedRealtimeNano(JNIEnv* env,
-        jobject clazz)
-{
-    return (jlong)elapsedRealtimeNano();
-}
-
-/*
  * JNI registration.
  */
 static const JNINativeMethod gMethods[] = {
-    /* name, signature, funcPtr */
-    { "uptimeMillis",      "!()J",
-            (void*) android_os_SystemClock_uptimeMillis },
-    { "elapsedRealtime",      "!()J",
-            (void*) android_os_SystemClock_elapsedRealtime },
-    { "currentThreadTimeMillis",      "!()J",
+    // All of these are @CriticalNative, so we can defer directly to SystemClock.h for
+    // some of these
+    { "uptimeMillis", "()J", (void*) uptimeMillis },
+    { "elapsedRealtime", "()J", (void*) elapsedRealtime },
+    { "elapsedRealtimeNanos", "()J", (void*) elapsedRealtimeNano },
+
+    // SystemClock doesn't have an implementation for these that we can directly call
+    { "currentThreadTimeMillis", "()J",
             (void*) android_os_SystemClock_currentThreadTimeMillis },
-    { "currentThreadTimeMicro",       "!()J",
+    { "currentThreadTimeMicro", "()J",
             (void*) android_os_SystemClock_currentThreadTimeMicro },
-    { "currentTimeMicro",             "!()J",
+    { "currentTimeMicro", "()J",
             (void*) android_os_SystemClock_currentTimeMicro },
-    { "elapsedRealtimeNanos",      "!()J",
-            (void*) android_os_SystemClock_elapsedRealtimeNano },
 };
 int register_android_os_SystemClock(JNIEnv* env)
 {
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index 30fc47b..ea893f0 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -110,27 +110,30 @@
     { "nativeGetEnabledTags",
             "()J",
             (void*)android_os_Trace_nativeGetEnabledTags },
-    { "nativeTraceCounter",
-            "!(JLjava/lang/String;I)V",
-            (void*)android_os_Trace_nativeTraceCounter },
-    { "nativeTraceBegin",
-            "!(JLjava/lang/String;)V",
-            (void*)android_os_Trace_nativeTraceBegin },
-    { "nativeTraceEnd",
-            "!(J)V",
-            (void*)android_os_Trace_nativeTraceEnd },
-    { "nativeAsyncTraceBegin",
-            "!(JLjava/lang/String;I)V",
-            (void*)android_os_Trace_nativeAsyncTraceBegin },
-    { "nativeAsyncTraceEnd",
-            "!(JLjava/lang/String;I)V",
-            (void*)android_os_Trace_nativeAsyncTraceEnd },
     { "nativeSetAppTracingAllowed",
             "(Z)V",
             (void*)android_os_Trace_nativeSetAppTracingAllowed },
     { "nativeSetTracingEnabled",
             "(Z)V",
             (void*)android_os_Trace_nativeSetTracingEnabled },
+
+    // ----------- @FastNative  ----------------
+
+    { "nativeTraceCounter",
+            "(JLjava/lang/String;I)V",
+            (void*)android_os_Trace_nativeTraceCounter },
+    { "nativeTraceBegin",
+            "(JLjava/lang/String;)V",
+            (void*)android_os_Trace_nativeTraceBegin },
+    { "nativeTraceEnd",
+            "(J)V",
+            (void*)android_os_Trace_nativeTraceEnd },
+    { "nativeAsyncTraceBegin",
+            "(JLjava/lang/String;I)V",
+            (void*)android_os_Trace_nativeAsyncTraceBegin },
+    { "nativeAsyncTraceEnd",
+            "(JLjava/lang/String;I)V",
+            (void*)android_os_Trace_nativeAsyncTraceEnd },
 };
 
 int register_android_os_Trace(JNIEnv* env) {
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index c4c1511..428159a 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -186,18 +186,19 @@
                 argv[argc++] = AssetManager::TARGET_APK_PATH;
                 argv[argc++] = AssetManager::IDMAP_DIR;
 
-                // Directories to scan for overlays: if OVERLAY_SUBDIR_PROPERTY is defined,
-                // use OVERLAY_SUBDIR/<value of OVERLAY_SUBDIR_PROPERTY>/ if exists, otherwise
+                // Directories to scan for overlays: if OVERLAY_SKU_DIR_PROPERTY is defined,
+                // use OVERLAY_DIR/<value of OVERLAY_SKU_DIR_PROPERTY> if exists, otherwise
                 // use OVERLAY_DIR if exists.
                 char subdir[PROP_VALUE_MAX];
-                int len = __system_property_get(AssetManager::OVERLAY_SUBDIR_PROPERTY, subdir);
+                int len = __system_property_get(AssetManager::OVERLAY_SKU_DIR_PROPERTY, subdir);
+                String8 overlayPath;
                 if (len > 0) {
-                    String8 subdirPath = String8(AssetManager::OVERLAY_SUBDIR) + "/" + subdir;
-                    if (stat(subdirPath.string(), &st) == 0) {
-                        argv[argc++] = subdirPath.string();
-                    }
-                } else if (stat(AssetManager::OVERLAY_DIR, &st) == 0) {
-                    argv[argc++] = AssetManager::OVERLAY_DIR;
+                    overlayPath = String8(AssetManager::OVERLAY_DIR) + "/" + subdir;
+                } else {
+                    overlayPath = String8(AssetManager::OVERLAY_DIR);
+                }
+                if (stat(overlayPath.string(), &st) == 0) {
+                    argv[argc++] = overlayPath.string();
                 }
 
                 // Finally, invoke idmap (if any overlay directory exists)
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index ea5a760..2eada3e 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -161,7 +161,8 @@
     { "nativeDispose",
             "(J)V",
             (void*)nativeDispose },
-    { "nativeScheduleVsync", "!(J)V",
+    // @FastNative
+    { "nativeScheduleVsync", "(J)V",
             (void*)nativeScheduleVsync }
 };
 
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 0245d38..2132f3d 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -383,17 +383,6 @@
     return 0;
 }
 
-static jlong android_view_MotionEvent_nativeCopy(JNIEnv* env, jclass clazz,
-        jlong destNativePtr, jlong sourceNativePtr, jboolean keepHistory) {
-    MotionEvent* destEvent = reinterpret_cast<MotionEvent*>(destNativePtr);
-    if (!destEvent) {
-        destEvent = new MotionEvent();
-    }
-    MotionEvent* sourceEvent = reinterpret_cast<MotionEvent*>(sourceNativePtr);
-    destEvent->copyFrom(sourceEvent, keepHistory);
-    return reinterpret_cast<jlong>(destEvent);
-}
-
 static void android_view_MotionEvent_nativeDispose(JNIEnv* env, jclass clazz,
         jlong nativePtr) {
     MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -426,228 +415,6 @@
     event->setMetaState(event->getMetaState() | metaState);
 }
 
-static jint android_view_MotionEvent_nativeGetDeviceId(JNIEnv* env, jclass clazz,
-        jlong nativePtr) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return event->getDeviceId();
-}
-
-static jint android_view_MotionEvent_nativeGetSource(JNIEnv* env, jclass clazz,
-        jlong nativePtr) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return event->getSource();
-}
-
-static void android_view_MotionEvent_nativeSetSource(JNIEnv* env, jclass clazz,
-        jlong nativePtr, jint source) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    event->setSource(source);
-}
-
-static jint android_view_MotionEvent_nativeGetAction(JNIEnv* env, jclass clazz,
-        jlong nativePtr) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return event->getAction();
-}
-
-static void android_view_MotionEvent_nativeSetAction(JNIEnv* env, jclass clazz,
-        jlong nativePtr, jint action) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    event->setAction(action);
-}
-
-static int android_view_MotionEvent_nativeGetActionButton(JNIEnv* env, jclass clazz,
-        jlong nativePtr) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return event->getActionButton();
-}
-
-static void android_view_MotionEvent_nativeSetActionButton(JNIEnv* env, jclass clazz,
-        jlong nativePtr, jint button) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    event->setActionButton(button);
-}
-
-static jboolean android_view_MotionEvent_nativeIsTouchEvent(JNIEnv* env, jclass clazz,
-        jlong nativePtr) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return event->isTouchEvent();
-}
-
-static jint android_view_MotionEvent_nativeGetFlags(JNIEnv* env, jclass clazz,
-        jlong nativePtr) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return event->getFlags();
-}
-
-static void android_view_MotionEvent_nativeSetFlags(JNIEnv* env, jclass clazz,
-        jlong nativePtr, jint flags) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    event->setFlags(flags);
-}
-
-static jint android_view_MotionEvent_nativeGetEdgeFlags(JNIEnv* env, jclass clazz,
-        jlong nativePtr) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return event->getEdgeFlags();
-}
-
-static void android_view_MotionEvent_nativeSetEdgeFlags(JNIEnv* env, jclass clazz,
-        jlong nativePtr, jint edgeFlags) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    event->setEdgeFlags(edgeFlags);
-}
-
-static jint android_view_MotionEvent_nativeGetMetaState(JNIEnv* env, jclass clazz,
-        jlong nativePtr) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return event->getMetaState();
-}
-
-static jint android_view_MotionEvent_nativeGetButtonState(JNIEnv* env, jclass clazz,
-        jlong nativePtr) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return event->getButtonState();
-}
-
-static void android_view_MotionEvent_nativeSetButtonState(JNIEnv* env, jclass clazz,
-        jlong nativePtr, jint buttonState) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    event->setButtonState(buttonState);
-}
-
-static void android_view_MotionEvent_nativeOffsetLocation(JNIEnv* env, jclass clazz,
-        jlong nativePtr, jfloat deltaX, jfloat deltaY) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return event->offsetLocation(deltaX, deltaY);
-}
-
-static jfloat android_view_MotionEvent_nativeGetXOffset(JNIEnv* env, jclass clazz,
-        jlong nativePtr) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return event->getXOffset();
-}
-
-static jfloat android_view_MotionEvent_nativeGetYOffset(JNIEnv* env, jclass clazz,
-        jlong nativePtr) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return event->getYOffset();
-}
-
-static jfloat android_view_MotionEvent_nativeGetXPrecision(JNIEnv* env, jclass clazz,
-        jlong nativePtr) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return event->getXPrecision();
-}
-
-static jfloat android_view_MotionEvent_nativeGetYPrecision(JNIEnv* env, jclass clazz,
-        jlong nativePtr) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return event->getYPrecision();
-}
-
-static jlong android_view_MotionEvent_nativeGetDownTimeNanos(JNIEnv* env, jclass clazz,
-        jlong nativePtr) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return event->getDownTime();
-}
-
-static void android_view_MotionEvent_nativeSetDownTimeNanos(JNIEnv* env, jclass clazz,
-        jlong nativePtr, jlong downTimeNanos) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    event->setDownTime(downTimeNanos);
-}
-
-static jint android_view_MotionEvent_nativeGetPointerCount(JNIEnv* env, jclass clazz,
-        jlong nativePtr) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return jint(event->getPointerCount());
-}
-
-static jint android_view_MotionEvent_nativeGetPointerId(JNIEnv* env, jclass clazz,
-        jlong nativePtr, jint pointerIndex) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    size_t pointerCount = event->getPointerCount();
-    if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
-        return -1;
-    }
-    return event->getPointerId(pointerIndex);
-}
-
-static jint android_view_MotionEvent_nativeGetToolType(JNIEnv* env, jclass clazz,
-        jlong nativePtr, jint pointerIndex) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    size_t pointerCount = event->getPointerCount();
-    if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
-        return -1;
-    }
-    return event->getToolType(pointerIndex);
-}
-
-static jint android_view_MotionEvent_nativeFindPointerIndex(JNIEnv* env, jclass clazz,
-        jlong nativePtr, jint pointerId) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return jint(event->findPointerIndex(pointerId));
-}
-
-static jint android_view_MotionEvent_nativeGetHistorySize(JNIEnv* env, jclass clazz,
-        jlong nativePtr) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    return jint(event->getHistorySize());
-}
-
-static jlong android_view_MotionEvent_nativeGetEventTimeNanos(JNIEnv* env, jclass clazz,
-        jlong nativePtr, jint historyPos) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    if (historyPos == HISTORY_CURRENT) {
-        return event->getEventTime();
-    } else {
-        size_t historySize = event->getHistorySize();
-        if (!validateHistoryPos(env, historyPos, historySize)) {
-            return 0;
-        }
-        return event->getHistoricalEventTime(historyPos);
-    }
-}
-
-static jfloat android_view_MotionEvent_nativeGetRawAxisValue(JNIEnv* env, jclass clazz,
-        jlong nativePtr, jint axis, jint pointerIndex, jint historyPos) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    size_t pointerCount = event->getPointerCount();
-    if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
-        return 0;
-    }
-
-    if (historyPos == HISTORY_CURRENT) {
-        return event->getRawAxisValue(axis, pointerIndex);
-    } else {
-        size_t historySize = event->getHistorySize();
-        if (!validateHistoryPos(env, historyPos, historySize)) {
-            return 0;
-        }
-        return event->getHistoricalRawAxisValue(axis, pointerIndex, historyPos);
-    }
-}
-
-static jfloat android_view_MotionEvent_nativeGetAxisValue(JNIEnv* env, jclass clazz,
-        jlong nativePtr, jint axis, jint pointerIndex, jint historyPos) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    size_t pointerCount = event->getPointerCount();
-    if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
-        return 0;
-    }
-
-    if (historyPos == HISTORY_CURRENT) {
-        return event->getAxisValue(axis, pointerIndex);
-    } else {
-        size_t historySize = event->getHistorySize();
-        if (!validateHistoryPos(env, historyPos, historySize)) {
-            return 0;
-        }
-        return event->getHistoricalAxisValue(axis, pointerIndex, historyPos);
-    }
-}
-
 static void android_view_MotionEvent_nativeGetPointerCoords(JNIEnv* env, jclass clazz,
         jlong nativePtr, jint pointerIndex, jint historyPos, jobject outPointerCoordsObj) {
     MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -684,30 +451,6 @@
     pointerPropertiesFromNative(env, pointerProperties, outPointerPropertiesObj);
 }
 
-static void android_view_MotionEvent_nativeScale(JNIEnv* env, jclass clazz,
-        jlong nativePtr, jfloat scale) {
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    event->scale(scale);
-}
-
-static void android_view_MotionEvent_nativeTransform(JNIEnv* env, jclass clazz,
-        jlong nativePtr, jobject matrixObj) {
-    SkMatrix* matrix = android_graphics_Matrix_getSkMatrix(env, matrixObj);
-    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-
-    float m[9];
-    m[0] = SkScalarToFloat(matrix->get(SkMatrix::kMScaleX));
-    m[1] = SkScalarToFloat(matrix->get(SkMatrix::kMSkewX));
-    m[2] = SkScalarToFloat(matrix->get(SkMatrix::kMTransX));
-    m[3] = SkScalarToFloat(matrix->get(SkMatrix::kMSkewY));
-    m[4] = SkScalarToFloat(matrix->get(SkMatrix::kMScaleY));
-    m[5] = SkScalarToFloat(matrix->get(SkMatrix::kMTransY));
-    m[6] = SkScalarToFloat(matrix->get(SkMatrix::kMPersp0));
-    m[7] = SkScalarToFloat(matrix->get(SkMatrix::kMPersp1));
-    m[8] = SkScalarToFloat(matrix->get(SkMatrix::kMPersp2));
-    event->transform(m);
-}
-
 static jlong android_view_MotionEvent_nativeReadFromParcel(JNIEnv* env, jclass clazz,
         jlong nativePtr, jobject parcelObj) {
     MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
@@ -750,6 +493,243 @@
     return static_cast<jint>(MotionEvent::getAxisFromLabel(axisLabel.c_str()));
 }
 
+// ---------------- @FastNative ----------------------------------
+
+static jint android_view_MotionEvent_nativeGetPointerId(JNIEnv* env, jclass clazz,
+        jlong nativePtr, jint pointerIndex) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    size_t pointerCount = event->getPointerCount();
+    if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
+        return -1;
+    }
+    return event->getPointerId(pointerIndex);
+}
+
+static jint android_view_MotionEvent_nativeGetToolType(JNIEnv* env, jclass clazz,
+        jlong nativePtr, jint pointerIndex) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    size_t pointerCount = event->getPointerCount();
+    if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
+        return -1;
+    }
+    return event->getToolType(pointerIndex);
+}
+
+static jlong android_view_MotionEvent_nativeGetEventTimeNanos(JNIEnv* env, jclass clazz,
+        jlong nativePtr, jint historyPos) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    if (historyPos == HISTORY_CURRENT) {
+        return event->getEventTime();
+    } else {
+        size_t historySize = event->getHistorySize();
+        if (!validateHistoryPos(env, historyPos, historySize)) {
+            return 0;
+        }
+        return event->getHistoricalEventTime(historyPos);
+    }
+}
+
+static jfloat android_view_MotionEvent_nativeGetRawAxisValue(JNIEnv* env, jclass clazz,
+        jlong nativePtr, jint axis,
+        jint pointerIndex, jint historyPos) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    size_t pointerCount = event->getPointerCount();
+    if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
+        return 0;
+    }
+
+    if (historyPos == HISTORY_CURRENT) {
+        return event->getRawAxisValue(axis, pointerIndex);
+    } else {
+        size_t historySize = event->getHistorySize();
+        if (!validateHistoryPos(env, historyPos, historySize)) {
+            return 0;
+        }
+        return event->getHistoricalRawAxisValue(axis, pointerIndex, historyPos);
+    }
+}
+
+static jfloat android_view_MotionEvent_nativeGetAxisValue(JNIEnv* env, jclass clazz,
+        jlong nativePtr, jint axis, jint pointerIndex, jint historyPos) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    size_t pointerCount = event->getPointerCount();
+    if (!validatePointerIndex(env, pointerIndex, pointerCount)) {
+        return 0;
+    }
+
+    if (historyPos == HISTORY_CURRENT) {
+        return event->getAxisValue(axis, pointerIndex);
+    } else {
+        size_t historySize = event->getHistorySize();
+        if (!validateHistoryPos(env, historyPos, historySize)) {
+            return 0;
+        }
+        return event->getHistoricalAxisValue(axis, pointerIndex, historyPos);
+    }
+}
+
+// ----------------- @CriticalNative ------------------------------
+
+static jlong android_view_MotionEvent_nativeCopy(jlong destNativePtr, jlong sourceNativePtr,
+        jboolean keepHistory) {
+    MotionEvent* destEvent = reinterpret_cast<MotionEvent*>(destNativePtr);
+    if (!destEvent) {
+        destEvent = new MotionEvent();
+    }
+    MotionEvent* sourceEvent = reinterpret_cast<MotionEvent*>(sourceNativePtr);
+    destEvent->copyFrom(sourceEvent, keepHistory);
+    return reinterpret_cast<jlong>(destEvent);
+}
+
+static jint android_view_MotionEvent_nativeGetDeviceId(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getDeviceId();
+}
+
+static jint android_view_MotionEvent_nativeGetSource(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getSource();
+}
+
+static void android_view_MotionEvent_nativeSetSource(jlong nativePtr, jint source) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    event->setSource(source);
+}
+
+static jint android_view_MotionEvent_nativeGetAction(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getAction();
+}
+
+static void android_view_MotionEvent_nativeSetAction(jlong nativePtr, jint action) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    event->setAction(action);
+}
+
+static int android_view_MotionEvent_nativeGetActionButton(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getActionButton();
+}
+
+static void android_view_MotionEvent_nativeSetActionButton(jlong nativePtr, jint button) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    event->setActionButton(button);
+}
+
+static jboolean android_view_MotionEvent_nativeIsTouchEvent(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->isTouchEvent();
+}
+
+static jint android_view_MotionEvent_nativeGetFlags(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getFlags();
+}
+
+static void android_view_MotionEvent_nativeSetFlags(jlong nativePtr, jint flags) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    event->setFlags(flags);
+}
+
+static jint android_view_MotionEvent_nativeGetEdgeFlags(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getEdgeFlags();
+}
+
+static void android_view_MotionEvent_nativeSetEdgeFlags(jlong nativePtr, jint edgeFlags) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    event->setEdgeFlags(edgeFlags);
+}
+
+static jint android_view_MotionEvent_nativeGetMetaState(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getMetaState();
+}
+
+static jint android_view_MotionEvent_nativeGetButtonState(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getButtonState();
+}
+
+static void android_view_MotionEvent_nativeSetButtonState(jlong nativePtr, jint buttonState) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    event->setButtonState(buttonState);
+}
+
+static void android_view_MotionEvent_nativeOffsetLocation(jlong nativePtr, jfloat deltaX,
+        jfloat deltaY) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->offsetLocation(deltaX, deltaY);
+}
+
+static jfloat android_view_MotionEvent_nativeGetXOffset(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getXOffset();
+}
+
+static jfloat android_view_MotionEvent_nativeGetYOffset(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getYOffset();
+}
+
+static jfloat android_view_MotionEvent_nativeGetXPrecision(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getXPrecision();
+}
+
+static jfloat android_view_MotionEvent_nativeGetYPrecision(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getYPrecision();
+}
+
+static jlong android_view_MotionEvent_nativeGetDownTimeNanos(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return event->getDownTime();
+}
+
+static void android_view_MotionEvent_nativeSetDownTimeNanos(jlong nativePtr, jlong downTimeNanos) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    event->setDownTime(downTimeNanos);
+}
+
+static jint android_view_MotionEvent_nativeGetPointerCount(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return jint(event->getPointerCount());
+}
+
+static jint android_view_MotionEvent_nativeFindPointerIndex(jlong nativePtr, jint pointerId) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return jint(event->findPointerIndex(pointerId));
+}
+
+static jint android_view_MotionEvent_nativeGetHistorySize(jlong nativePtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    return jint(event->getHistorySize());
+}
+
+static void android_view_MotionEvent_nativeScale(jlong nativePtr, jfloat scale) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    event->scale(scale);
+}
+
+static void android_view_MotionEvent_nativeTransform(jlong nativePtr, jlong matrixPtr) {
+    MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
+    SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
+
+    static_assert(SkMatrix::kMScaleX == 0, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMSkewX == 1, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMTransX == 2, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMSkewY == 3, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMScaleY == 4, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMTransY == 5, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMPersp0 == 6, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMPersp1 == 7, "SkMatrix unexpected index");
+    static_assert(SkMatrix::kMPersp2 == 8, "SkMatrix unexpected index");
+    float m[9];
+    matrix->get9(m);
+    event->transform(m);
+}
+
 // ----------------------------------------------------------------------------
 
 static const JNINativeMethod gMotionEventMethods[] = {
@@ -758,117 +738,12 @@
             "(JIIIIIIIFFFFJJI[Landroid/view/MotionEvent$PointerProperties;"
                     "[Landroid/view/MotionEvent$PointerCoords;)J",
             (void*)android_view_MotionEvent_nativeInitialize },
-    { "nativeCopy",
-            "(JJZ)J",
-            (void*)android_view_MotionEvent_nativeCopy },
     { "nativeDispose",
             "(J)V",
             (void*)android_view_MotionEvent_nativeDispose },
     { "nativeAddBatch",
             "(JJ[Landroid/view/MotionEvent$PointerCoords;I)V",
             (void*)android_view_MotionEvent_nativeAddBatch },
-    { "nativeGetDeviceId",
-            "!(J)I",
-            (void*)android_view_MotionEvent_nativeGetDeviceId },
-    { "nativeGetSource",
-            "!(J)I",
-            (void*)android_view_MotionEvent_nativeGetSource },
-    { "nativeSetSource",
-            "!(JI)I",
-            (void*)android_view_MotionEvent_nativeSetSource },
-    { "nativeGetAction",
-            "!(J)I",
-            (void*)android_view_MotionEvent_nativeGetAction },
-    { "nativeSetAction",
-            "!(JI)V",
-            (void*)android_view_MotionEvent_nativeSetAction },
-    { "nativeGetActionButton",
-            "!(J)I",
-            (void*)android_view_MotionEvent_nativeGetActionButton},
-    { "nativeSetActionButton",
-            "!(JI)V",
-            (void*)android_view_MotionEvent_nativeSetActionButton},
-    { "nativeIsTouchEvent",
-            "!(J)Z",
-            (void*)android_view_MotionEvent_nativeIsTouchEvent },
-    { "nativeGetFlags",
-            "!(J)I",
-            (void*)android_view_MotionEvent_nativeGetFlags },
-    { "nativeSetFlags",
-            "!(JI)V",
-            (void*)android_view_MotionEvent_nativeSetFlags },
-    { "nativeGetEdgeFlags",
-            "!(J)I",
-            (void*)android_view_MotionEvent_nativeGetEdgeFlags },
-    { "nativeSetEdgeFlags",
-            "!(JI)V",
-            (void*)android_view_MotionEvent_nativeSetEdgeFlags },
-    { "nativeGetMetaState",
-            "!(J)I",
-            (void*)android_view_MotionEvent_nativeGetMetaState },
-    { "nativeGetButtonState",
-            "!(J)I",
-            (void*)android_view_MotionEvent_nativeGetButtonState },
-    { "nativeSetButtonState",
-            "!(JI)V",
-            (void*)android_view_MotionEvent_nativeSetButtonState },
-    { "nativeOffsetLocation",
-            "!(JFF)V",
-            (void*)android_view_MotionEvent_nativeOffsetLocation },
-    { "nativeGetXOffset",
-            "!(J)F",
-            (void*)android_view_MotionEvent_nativeGetXOffset },
-    { "nativeGetYOffset",
-            "!(J)F",
-            (void*)android_view_MotionEvent_nativeGetYOffset },
-    { "nativeGetXPrecision",
-            "!(J)F",
-            (void*)android_view_MotionEvent_nativeGetXPrecision },
-    { "nativeGetYPrecision",
-            "!(J)F",
-            (void*)android_view_MotionEvent_nativeGetYPrecision },
-    { "nativeGetDownTimeNanos",
-            "!(J)J",
-            (void*)android_view_MotionEvent_nativeGetDownTimeNanos },
-    { "nativeSetDownTimeNanos",
-            "!(JJ)V",
-            (void*)android_view_MotionEvent_nativeSetDownTimeNanos },
-    { "nativeGetPointerCount",
-            "!(J)I",
-            (void*)android_view_MotionEvent_nativeGetPointerCount },
-    { "nativeGetPointerId",
-            "!(JI)I",
-            (void*)android_view_MotionEvent_nativeGetPointerId },
-    { "nativeGetToolType",
-            "!(JI)I",
-            (void*)android_view_MotionEvent_nativeGetToolType },
-    { "nativeFindPointerIndex",
-            "!(JI)I",
-            (void*)android_view_MotionEvent_nativeFindPointerIndex },
-    { "nativeGetHistorySize",
-            "!(J)I",
-            (void*)android_view_MotionEvent_nativeGetHistorySize },
-    { "nativeGetEventTimeNanos",
-            "!(JI)J",
-            (void*)android_view_MotionEvent_nativeGetEventTimeNanos },
-    { "nativeGetRawAxisValue",
-            "!(JIII)F",
-            (void*)android_view_MotionEvent_nativeGetRawAxisValue },
-    { "nativeGetAxisValue",
-            "!(JIII)F",
-            (void*)android_view_MotionEvent_nativeGetAxisValue },
-    { "nativeGetPointerCoords",
-            "(JIILandroid/view/MotionEvent$PointerCoords;)V",
-            (void*)android_view_MotionEvent_nativeGetPointerCoords },
-    { "nativeGetPointerProperties",
-            "(JILandroid/view/MotionEvent$PointerProperties;)V",
-            (void*)android_view_MotionEvent_nativeGetPointerProperties },
-    { "nativeScale",
-            "!(JF)V",
-            (void*)android_view_MotionEvent_nativeScale },
-    { "nativeTransform",
-            "(JLandroid/graphics/Matrix;)V",
-            (void*)android_view_MotionEvent_nativeTransform },
     { "nativeReadFromParcel",
             "(JLandroid/os/Parcel;)J",
             (void*)android_view_MotionEvent_nativeReadFromParcel },
@@ -879,6 +754,116 @@
             (void*)android_view_MotionEvent_nativeAxisToString },
     { "nativeAxisFromString", "(Ljava/lang/String;)I",
             (void*)android_view_MotionEvent_nativeAxisFromString },
+    { "nativeGetPointerProperties",
+            "(JILandroid/view/MotionEvent$PointerProperties;)V",
+            (void*)android_view_MotionEvent_nativeGetPointerProperties },
+    { "nativeGetPointerCoords",
+            "(JIILandroid/view/MotionEvent$PointerCoords;)V",
+            (void*)android_view_MotionEvent_nativeGetPointerCoords },
+
+    // --------------- @FastNative ----------------------
+    { "nativeGetPointerId",
+            "(JI)I",
+            (void*)android_view_MotionEvent_nativeGetPointerId },
+    { "nativeGetToolType",
+            "(JI)I",
+            (void*)android_view_MotionEvent_nativeGetToolType },
+    { "nativeGetEventTimeNanos",
+            "(JI)J",
+            (void*)android_view_MotionEvent_nativeGetEventTimeNanos },
+    { "nativeGetRawAxisValue",
+            "(JIII)F",
+            (void*)android_view_MotionEvent_nativeGetRawAxisValue },
+    { "nativeGetAxisValue",
+            "(JIII)F",
+            (void*)android_view_MotionEvent_nativeGetAxisValue },
+
+    // --------------- @CriticalNative ------------------
+
+    { "nativeCopy",
+            "(JJZ)J",
+            (void*)android_view_MotionEvent_nativeCopy },
+    { "nativeGetDeviceId",
+            "(J)I",
+            (void*)android_view_MotionEvent_nativeGetDeviceId },
+    { "nativeGetSource",
+            "(J)I",
+            (void*)android_view_MotionEvent_nativeGetSource },
+    { "nativeSetSource",
+            "(JI)I",
+            (void*)android_view_MotionEvent_nativeSetSource },
+    { "nativeGetAction",
+            "(J)I",
+            (void*)android_view_MotionEvent_nativeGetAction },
+    { "nativeSetAction",
+            "(JI)V",
+            (void*)android_view_MotionEvent_nativeSetAction },
+    { "nativeGetActionButton",
+            "(J)I",
+            (void*)android_view_MotionEvent_nativeGetActionButton},
+    { "nativeSetActionButton",
+            "(JI)V",
+            (void*)android_view_MotionEvent_nativeSetActionButton},
+    { "nativeIsTouchEvent",
+            "(J)Z",
+            (void*)android_view_MotionEvent_nativeIsTouchEvent },
+    { "nativeGetFlags",
+            "(J)I",
+            (void*)android_view_MotionEvent_nativeGetFlags },
+    { "nativeSetFlags",
+            "(JI)V",
+            (void*)android_view_MotionEvent_nativeSetFlags },
+    { "nativeGetEdgeFlags",
+            "(J)I",
+            (void*)android_view_MotionEvent_nativeGetEdgeFlags },
+    { "nativeSetEdgeFlags",
+            "(JI)V",
+            (void*)android_view_MotionEvent_nativeSetEdgeFlags },
+    { "nativeGetMetaState",
+            "(J)I",
+            (void*)android_view_MotionEvent_nativeGetMetaState },
+    { "nativeGetButtonState",
+            "(J)I",
+            (void*)android_view_MotionEvent_nativeGetButtonState },
+    { "nativeSetButtonState",
+            "(JI)V",
+            (void*)android_view_MotionEvent_nativeSetButtonState },
+    { "nativeOffsetLocation",
+            "(JFF)V",
+            (void*)android_view_MotionEvent_nativeOffsetLocation },
+    { "nativeGetXOffset",
+            "(J)F",
+            (void*)android_view_MotionEvent_nativeGetXOffset },
+    { "nativeGetYOffset",
+            "(J)F",
+            (void*)android_view_MotionEvent_nativeGetYOffset },
+    { "nativeGetXPrecision",
+            "(J)F",
+            (void*)android_view_MotionEvent_nativeGetXPrecision },
+    { "nativeGetYPrecision",
+            "(J)F",
+            (void*)android_view_MotionEvent_nativeGetYPrecision },
+    { "nativeGetDownTimeNanos",
+            "(J)J",
+            (void*)android_view_MotionEvent_nativeGetDownTimeNanos },
+    { "nativeSetDownTimeNanos",
+            "(JJ)V",
+            (void*)android_view_MotionEvent_nativeSetDownTimeNanos },
+    { "nativeGetPointerCount",
+            "(J)I",
+            (void*)android_view_MotionEvent_nativeGetPointerCount },
+    { "nativeFindPointerIndex",
+            "(JI)I",
+            (void*)android_view_MotionEvent_nativeFindPointerIndex },
+    { "nativeGetHistorySize",
+            "(J)I",
+            (void*)android_view_MotionEvent_nativeGetHistorySize },
+    { "nativeScale",
+            "(JF)V",
+            (void*)android_view_MotionEvent_nativeScale },
+    { "nativeTransform",
+            "(JJ)V",
+            (void*)android_view_MotionEvent_nativeTransform },
 };
 
 int register_android_view_MotionEvent(JNIEnv* env) {
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 649cc1e..309bb2f 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -954,7 +954,7 @@
 const char* const kClassPathName = "android/view/ThreadedRenderer";
 
 static const JNINativeMethod gMethods[] = {
-    { "nSupportsOpenGL", "!()Z", (void*) android_view_ThreadedRenderer_supportsOpenGL },
+    { "nSupportsOpenGL", "()Z", (void*) android_view_ThreadedRenderer_supportsOpenGL },
     { "nSetAtlas", "(JLandroid/view/GraphicBuffer;[J)V",   (void*) android_view_ThreadedRenderer_setAtlas },
     { "nSetProcessStatsBuffer", "(JI)V", (void*) android_view_ThreadedRenderer_setProcessStatsBuffer },
     { "nGetRenderThreadTid", "(J)I", (void*) android_view_ThreadedRenderer_getRenderThreadTid },
diff --git a/core/jni/fd_utils-inl.h b/core/jni/fd_utils-inl.h
index 5c17b23..af27069 100644
--- a/core/jni/fd_utils-inl.h
+++ b/core/jni/fd_utils-inl.h
@@ -260,16 +260,13 @@
 
     // Whitelist files needed for Runtime Resource Overlay, like these:
     // /system/vendor/overlay/framework-res.apk
-    // /system/vendor/overlay-subdir/pg/framework-res.apk
+    // /system/vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
     // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
-    // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
-    // See AssetManager.cpp for more details on overlay-subdir.
+    // /data/resource-cache/system@vendor@overlay@PG@framework-res.apk@idmap
     static const char* kOverlayDir = "/system/vendor/overlay/";
-    static const char* kOverlaySubdir = "/system/vendor/overlay-subdir/";
     static const char* kApkSuffix = ".apk";
 
-    if ((android::base::StartsWith(path, kOverlayDir)
-            || android::base::StartsWith(path, kOverlaySubdir))
+    if (android::base::StartsWith(path, kOverlayDir)
         && android::base::EndsWith(path, kApkSuffix)
         && path.find("/../") == std::string::npos) {
       return true;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 357d6f9..d4bef17 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -290,6 +290,7 @@
     <protected-broadcast android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.WIFI_CREDENTIAL_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.WIFI_SCAN_AVAILABLE" />
+    <protected-broadcast android:name="android.net.wifi.nan.action.WIFI_NAN_STATE_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.SCAN_RESULTS" />
     <protected-broadcast android:name="android.net.wifi.RSSI_CHANGED" />
     <protected-broadcast android:name="android.net.wifi.STATE_CHANGE" />
@@ -495,6 +496,8 @@
 
     <protected-broadcast android:name="com.android.server.retaildemo.ACTION_RESET_DEMO" />
 
+    <protected-broadcast android:name="android.intent.action.DEVICE_LOCKED_CHANGED" />
+
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
diff --git a/core/res/res/color/background_cache_hint_selector_device_default.xml b/core/res/res/color/background_cache_hint_selector_device_default.xml
new file mode 100644
index 0000000..3cb4bbc
--- /dev/null
+++ b/core/res/res/color/background_cache_hint_selector_device_default.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_accelerated="false" android:color="?attr/colorBackground" />
+    <item android:color="@android:color/transparent" />
+</selector>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 34244ab..5ca9f43 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Foonopsies"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Skermslot"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Sit af"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Noodgeval"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Foutverslag"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Neem foutverslag"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Dit sal inligting oor die huidige toestand van jou toestel insamel om as \'n e-posboodskap te stuur. Dit sal \'n tydjie neem vandat die foutverslag begin is totdat dit reg is om gestuur te word; wees asseblief geduldig."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g>-USB-datastokkie"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB-berging"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Redigeer"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Datagebruik-opletberig"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Tik om gebruik en instellings te bekyk."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G-datalimiet bereik"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G-datalimiet bereik"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index b3cd896..18fc6dd 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"የስልክ አማራጮች"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"ማያ ቆልፍ"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"ኃይል አጥፋ"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"ድንገተኛ አደጋ"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"የሳንካ ሪፖርት"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"የሳንካ ሪፖርት ውሰድ"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ይሄ እንደ የኢሜይል መልዕክት አድርጎ የሚልከውን ስለመሣሪያዎ የአሁኑ ሁኔታ መረጃ ይሰበስባል። የሳንካ ሪፖርቱን ከመጀመር ጀምሮ እስኪላክ ድረስ ትንሽ ጊዜ ይወስዳል፤ እባክዎ ይታገሱ።"</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"የ<xliff:g id="MANUFACTURER">%s</xliff:g> ዩኤስቢ አንጻፊ"</string>
     <string name="storage_usb" msgid="3017954059538517278">"የUSB  ማከማቻ"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"አርትዕ"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"የውሂብ አጠቃቀም ማንቂያ"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"አጠቃቀምን እና ቅንብሮችን ለማየት መታ ያድርጉ።"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"የ2ጂ-3ጂ ውሂብ ገደብ ላይ ተደርሷል"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"የ4ጂ ውሂብ ገደብ ላይ ተደርሷል"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 717e9da..3451bcc 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -222,8 +222,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"خيارات الهاتف"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"تأمين الشاشة"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"إيقاف التشغيل"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"الطوارئ"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"تقرير الأخطاء"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"إعداد تقرير بالأخطاء"</string>
     <string name="bugreport_message" msgid="398447048750350456">"سيجمع هذا معلومات حول حالة جهازك الحالي لإرسالها كرسالة إلكترونية، ولكنه سيستغرق وقتًا قليلاً من بدء عرض تقرير بالأخطاء. وحتى يكون جاهزًا للإرسال، الرجاء الانتظار."</string>
@@ -1434,8 +1433,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"‏محرك أقراص USB من <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"‏وحدة تخزين USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"تعديل"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"تنبيه استخدام البيانات"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"انقر لعرض الاستخدام والإعدادات."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"‏تم بلوغ حد بيانات اتصال 2G-3G"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"‏تم بلوغ حد بيانات اتصال 4G"</string>
diff --git a/core/res/res/values-az-rAZ/strings.xml b/core/res/res/values-az-rAZ/strings.xml
index 5ff1373..1f0d164 100644
--- a/core/res/res/values-az-rAZ/strings.xml
+++ b/core/res/res/values-az-rAZ/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefon seçimləri"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Ekran kilidi"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Söndür"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Təcili"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Baq hesabatı"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Baqı xəbər verin"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Bu, sizin hazırkı cihaz durumu haqqında məlumat toplayacaq ki, elektron məktub şəklində göndərsin. Baq raportuna başlamaq üçün bir az vaxt lazım ola bilər, bir az səbr edin."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB drayv"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB yaddaş"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Düzəliş edin"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Data istifadə siqnalı"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"İstifadə və ayarları görmək üçün tıklayın."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G data limitinə çatdı"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G data limitinə çatdı"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index a351d5c..3469f97 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -216,8 +216,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opcije telefona"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Zaključaj ekran"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Isključi"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Hitni poziv"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Izveštaj o grešci"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Napravi izveštaj o grešci"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Ovim će se prikupiti informacije o trenutnom stanju uređaja kako bi bile poslate u poruci e-pošte. Od započinjanja izveštaja o grešci do trenutka za njegovo slanje proći će neko vreme; budite strpljivi."</string>
@@ -1356,8 +1355,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB disk"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB memorija"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Izmeni"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Obaveštenje o potrošnji podataka"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Dodirnite za potrošnju i podešavanja."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Nema više 2G-3G podataka"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Nema više 4G podataka"</string>
diff --git a/core/res/res/values-be-rBY/strings.xml b/core/res/res/values-be-rBY/strings.xml
index 13a9cdcef..f5f1e62 100644
--- a/core/res/res/values-be-rBY/strings.xml
+++ b/core/res/res/values-be-rBY/strings.xml
@@ -218,8 +218,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Параметры тэлефона"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Блакіроўка экрана"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Выключыць"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"SOS-выклік"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Справаздача пра памылкі"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Справаздача пра памылку"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Будзе збiрацца iнфармацыя пра бягучы стан прылады, якая будзе адпраўляцца на электронную пошту. Стварэнне справаздачы пра памылкi зойме некаторы час."</string>
@@ -1382,8 +1381,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"USB-дыск <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB-назапашвальнік"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Рэдагаваць"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Абвестка аб выкарыстанні трафіка"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Прагляд выкарыстання і налад."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Дасягнуты ліміт трафіку 2G-3G"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Дасягнуты ліміт трафіку 4G"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 95e89af..f6eb810 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Опции на телефона"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Заключване на екрана"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Изключване"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Спешно обаждане"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Сигнал за програмна грешка"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Сигнал за програмна грешка"</string>
     <string name="bugreport_message" msgid="398447048750350456">"По този начин ще се събере информация за текущото състояние на устройството ви, която да се изпрати като имейл съобщение. След стартирането на процеса ще мине известно време, докато сигналът за програмна грешка бъде готов за подаване. Моля, имайте търпение."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"USB устройство от <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB хранилище"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Редактиране"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Сигнал за преноса на данни"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Пренос и настройки: Докоснете за преглед."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Достигнат лимит за 2G/3G данните"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Достигнат лимит за 4G данните"</string>
diff --git a/core/res/res/values-bn-rBD/strings.xml b/core/res/res/values-bn-rBD/strings.xml
index 25f3367..f873b1c 100644
--- a/core/res/res/values-bn-rBD/strings.xml
+++ b/core/res/res/values-bn-rBD/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ফোন বিকল্পগুলি"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"স্ক্রীণ লক"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"পাওয়ার বন্ধ করুন"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"জরুরী"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"ত্রুটির প্রতিবেদন"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"ত্রুটির অভিযোগ করুন"</string>
     <string name="bugreport_message" msgid="398447048750350456">"এটি একটি ই-মেল বার্তা পাঠানোর জন্য আপনার ডিভাইসের বর্তমান অবস্থা সম্পর্কে তথ্য সংগ্রহ করবে৷ ত্রুটির প্রতিবেদন শুরুর সময় থেকে এটি পাঠানোর জন্য প্রস্তুত হতে কিছুটা সময় নেবে; দয়া করে ধৈর্য রাখুন৷"</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB ড্রাইভ"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB সঞ্চয়স্থান"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"সম্পাদনা করুন"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"ডেটা ব্যবহারের সতর্কতা"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"ব্যবহার এবং সেটিংস দেখতে আলতো চাপুন৷"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G ডেটা সীমা ছাড়িয়েছে"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G ডেটা সীমা ছাড়িয়েছে"</string>
diff --git a/core/res/res/values-bs-rBA/strings.xml b/core/res/res/values-bs-rBA/strings.xml
index e786ff9..7cb46fe 100644
--- a/core/res/res/values-bs-rBA/strings.xml
+++ b/core/res/res/values-bs-rBA/strings.xml
@@ -216,8 +216,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opcije telefona"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Zaključavanje ekrana"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Isključi telefon"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Hitni slučaj"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Izvještaj o greškama"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Kreirajte izvještaj o greškama"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Ovim će se prikupljati informacije o trenutnom stanju uređaja, koji će biti poslani kao poruka e-pošte. Može malo potrajati dok se izvještaj o greškama ne kreira i bude spreman za slanje. Budite strpljivi."</string>
@@ -1358,8 +1357,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB disk"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB pohrana"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Uredi"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Upozorenje o prijenosu podataka"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Dodirnite za prikaz upotrebe i postavki."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Dostignut limit za 2G-3G podatke"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Dostignut limit za 4G podatke"</string>
@@ -1600,7 +1598,7 @@
     <string name="package_updated_device_owner" msgid="8856631322440187071">"Ažurirao administrator"</string>
     <string name="package_deleted_device_owner" msgid="7650577387493101353">"Izbrisao administrator"</string>
     <string name="battery_saver_description" msgid="1960431123816253034">"Da bi se trajanje baterije produžilo, opcija za štednju baterije minimizira rad uređaja i ograničava vibriranje, usluge lokacije i većinu prijenosa podataka u pozadini. E-pošta, poruke i druge aplikacije koje se oslanjaju na sinhronizaciju ne mogu biti ažurirane dok ih ne otvorite.\n\nŠtednja baterije se automatski isključi prilikom punjenja uređaja."</string>
-    <string name="data_saver_description" msgid="6015391409098303235">"Da bi se smanjilo korištenje podataka, usluga Ušteda podataka sprečava da neke aplikacije šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može pristupiti podacima, ali se to može desiti rjeđe. To može značiti, naprimjer, da se slike ne prikazuju sve dok ih ne dodirnete."</string>
+    <string name="data_saver_description" msgid="6015391409098303235">"Da bi se smanjio prijenos podataka, usluga Ušteda podataka sprečava da neke aplikacije šalju ili primaju podatke u pozadini. Aplikacija koju trenutno koristite može pristupiti podacima, ali se to može desiti rjeđe. To može značiti, naprimjer, da se slike ne prikazuju sve dok ih ne dodirnete."</string>
     <string name="data_saver_enable_title" msgid="4674073932722787417">"Uključiti Uštedu podataka?"</string>
     <string name="data_saver_enable_button" msgid="7147735965247211818">"Uključi"</string>
     <plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="4367877408072000848">
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index ac0b6d3..d1fbebb 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opcions del telèfon"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Bloqueig de pantalla"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Apaga"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergències"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Informe d\'error"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Crea informe d\'errors"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Es recopilarà informació sobre l\'estat actual del dispositiu i se t\'enviarà per correu electrònic. Passaran uns quants minuts des de l\'inici de l\'informe d\'errors fins al seu enviament, per la qual cosa et recomanem que tinguis paciència."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Unitat USB de: <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Emmagatzematge USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Edita"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Alerta d\'ús de dades"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Toca per veure l\'ús i la configuració."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Límit de dades 2G-3G assolit"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Límit de dades 4G assolit"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 4370d9f..0e6ba82 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -218,8 +218,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Možnosti telefonu"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Zámek obrazovky"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Vypnout"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Stav nouze"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Hlášení chyb"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Vytvořit chybové hlášení"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Shromažďuje informace o aktuálním stavu zařízení. Tyto informace je následně možné poslat v e-mailové zprávě, chvíli však potrvá, než bude hlášení o chybě připraveno k odeslání. Buďte prosím trpěliví."</string>
@@ -1382,8 +1381,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Jednotka USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Úložiště USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Upravit"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Upozornění na používání dat"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Klepnutím zobrazíte nastavení."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Dosáhli jste limitu dat 2G–3G"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Dosáhli jste limitu dat 4G"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 1688cbe..628f9bb 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Indstillinger for telefon"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Skærmlås"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Sluk"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Nødopkald"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Fejlrapport"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Lav fejlrapport"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Der indsamles oplysninger om din enheds aktuelle status, der efterfølgende sendes i en e-mail. Der går lidt tid, fra fejlrapporten påbegyndes, til den er klar til at blive sendt. Tak for tålmodigheden."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"USB-drev fra <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB-lager"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Rediger"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Underretning om dataforbrug"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Tryk for at se forbrug og indstillinger."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Grænsen for 2G-3G-data er nået"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Grænsen for 4G-data er nået"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index d13777b..f453024 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefonoptionen"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Displaysperre"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Ausschalten"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Notfall"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Fehlerbericht"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Fehlerbericht abrufen"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Bei diesem Fehlerbericht werden Daten zum aktuellen Status deines Geräts erfasst und als E-Mail versandt. Vom Start des Berichts bis zu seinem Versand kann es eine Weile dauern. Bitte habe etwas Geduld."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"USB-Speichergerät von <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB-Speicher"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Bearbeiten"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Warnung zur Datennutzung"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Für Nutzung und Einstellungen tippen."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-/3G-Datenlimit erreicht"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G-Datenlimit erreicht"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 8f63a73..5f148e3 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Επιλογές τηλεφώνου"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Κλείδωμα οθόνης"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Απενεργοποίηση"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Κλήση έκτακτης ανάγκης"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Αναφορά σφαλμάτων"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Λήψη αναφοράς σφάλματος"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Θα συλλέξει πληροφορίες σχετικά με την τρέχουσα κατάσταση της συσκευής σας και θα τις στείλει μέσω μηνύματος ηλεκτρονικού ταχυδρομείου. Απαιτείται λίγος χρόνος για τη σύνταξη της αναφοράς σφάλματος και την αποστολή της. Κάντε λίγη υπομονή."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Μονάδα USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Αποθηκευτικός χώρος USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Επεξεργασία"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Ειδοποίηση χρήσης δεδομένων"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Πατήστε για προβολή χρήσης/ρυθμ."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Συμπλ. το όριο δεδομένων 2G-3G"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Συμπλ. το όριο δεδομένων 4G"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 348a7de56..38f2b88 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Phone options"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Screen lock"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Power off"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Bug report"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Take bug report"</string>
     <string name="bugreport_message" msgid="398447048750350456">"This will collect information about your current device state, to send as an email message. It will take a little time from starting the bug report until it is ready to be sent. Please be patient."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB drive"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB storage"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Edit"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Data usage alert"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Tap to view usage and settings."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G data limit reached"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G data limit reached"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 348a7de56..38f2b88 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Phone options"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Screen lock"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Power off"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Bug report"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Take bug report"</string>
     <string name="bugreport_message" msgid="398447048750350456">"This will collect information about your current device state, to send as an email message. It will take a little time from starting the bug report until it is ready to be sent. Please be patient."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB drive"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB storage"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Edit"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Data usage alert"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Tap to view usage and settings."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G data limit reached"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G data limit reached"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 348a7de56..38f2b88 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Phone options"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Screen lock"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Power off"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Bug report"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Take bug report"</string>
     <string name="bugreport_message" msgid="398447048750350456">"This will collect information about your current device state, to send as an email message. It will take a little time from starting the bug report until it is ready to be sent. Please be patient."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB drive"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB storage"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Edit"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Data usage alert"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Tap to view usage and settings."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G data limit reached"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G data limit reached"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 39746e0..1277acc 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opciones de dispositivo"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Bloqueo de pantalla"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Apagar"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergencias"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Informe de errores"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Iniciar informe de errores"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Se recopilará información sobre el estado actual de tu dispositivo, que se enviará por correo. Pasarán unos minutos desde que se inicie el informe de errores hasta que se envíe, por lo que te recomendamos que tengas paciencia."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Unidad USB de <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Almacenamiento USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Editar"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Alerta por el uso de datos"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Presiona para uso y opciones."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Límite de datos 2G-3G alcanzado"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Límite de datos 4G alcanzado"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 117205f..7cd4ad5 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opciones del teléfono"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Bloqueo de pantalla"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Apagar"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergencia"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Informe de error"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Crear informe de errores"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Se recopilará información sobre el estado actual de tu dispositivo y se enviará por correo electrónico. Pasarán unos minutos desde que empiece a generarse el informe de errores hasta que se envíe."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Unidad USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Almacenamiento USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Editar"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Alerta sobre el uso de datos"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Toca para ver uso y ajustes."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Límite de datos 2G-3G alcanzado"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Límite de datos 4G alcanzado"</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 2fadecb..d1a3215 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefonivalikud"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Ekraanilukk"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Lülita välja"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Hädaabi"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Veaaruanne"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Veaaruande võtmine"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Nii kogutakse teavet teie seadme praeguse oleku kohta, et saata see meilisõnumina. Enne kui saate veaaruande ära saata, võtab selle loomine natuke aega; varuge kannatust."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Tootja <xliff:g id="MANUFACTURER">%s</xliff:g> USB-ketas"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB-mäluseade"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Muuda"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Andmekasutuse hoiatus"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Puudutage kasutuse/seadete vaat."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-, 3G-andmeside limiit on täis"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G-andmeside limiit on täis"</string>
diff --git a/core/res/res/values-eu-rES/strings.xml b/core/res/res/values-eu-rES/strings.xml
index 6179987..da65093 100644
--- a/core/res/res/values-eu-rES/strings.xml
+++ b/core/res/res/values-eu-rES/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefonoaren aukerak"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Pantailaren blokeoa"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Itzali"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Larrialdiak"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Akatsen txostena"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Sortu akatsen txostena"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Gailuaren uneko egoerari buruzko informazioa bilduko da, mezu elektroniko gisa bidaltzeko. Minutu batzuk igaroko dira akatsen txostena sortzen hasten denetik bidaltzeko prest egon arte. Itxaron, mesedez."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB unitatea"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB memoria"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Editatu"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Datu-erabilerari buruzko abisua"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Sakatu erabilera eta ezarpenak ikusteko."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2-3 GB-ko mugara iritsi zara"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4 GB-ko mugara iritsi zara"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index fd2bf81..374183f 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"گزینه‌های تلفن"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"قفل صفحه"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"خاموش کردن"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"اضطراری"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"گزارش اشکال"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"گرفتن گزارش اشکال"</string>
     <string name="bugreport_message" msgid="398447048750350456">"این گزارش اطلاعات مربوط به وضعیت دستگاه کنونی شما را جمع‌آوری می‌کند تا به صورت یک پیام رایانامه ارسال شود. از زمان شروع گزارش اشکال تا آماده شدن برای ارسال اندکی زمان می‌برد؛ لطفاً شکیبا باشید."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"‏درایو USB ‏<xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"‏حافظهٔ USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"ویرایش"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"هشدار مصرف داده"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"برای مشاهده مصرف و تنظیمات ضربه بزنید."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"‏به حد مجاز مصرف داده 2G-3G رسید"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"‏به حد مجاز مصرف داده 4G رسید"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 3ea7e66..9b6d57c 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Puhelimen asetukset"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Näytön lukitus"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Katkaise virta"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Hätäpuhelu"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Virheraportti"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Luo virheraportti"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Toiminto kerää tietoja laitteen tilasta ja lähettää ne sähköpostitse. Virheraportti on valmis lähetettäväksi hetken kuluttua - kiitos kärsivällisyydestäsi."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"USB-asema: <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB-tallennustila"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Muokkaa"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Datankäyttövaroitus"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Käyttö &amp; asetukset napauttamalla"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G-tietojen raja saavutettu"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G-tietojen raja saavutettu"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 8b74244..034b291 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Options du téléphone"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Verrouillage de l\'écran"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Éteindre"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Urgence"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Rapport de bogue"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Créer un rapport de bogue"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Cela permet de recueillir des informations concernant l\'état actuel de votre appareil. Ces informations sont ensuite envoyées sous forme de courriel. Merci de patienter pendant la préparation du rapport de bogue. Cette opération peut prendre quelques instants."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Clé USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Mémoire de stockage USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Modifier"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Alerte d\'utilisation des données"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Touch. pour aff. util. et param."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Limite de données 2G-3G atteinte"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Limite de données 4G atteinte"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 8c8c683..6dfa504 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Options du téléphone"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Verrouillage de l\'écran"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Éteindre"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Urgences"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Rapport de bug"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Créer un rapport de bug"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Cela permet de recueillir des informations concernant l\'état actuel de votre appareil. Ces informations sont ensuite envoyées sous forme d\'e-mail. Merci de patienter pendant la préparation du rapport de bug. Cette opération peut prendre quelques instants."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Clé USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Mémoire de stockage USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Modifier"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Alerte de consommation des données"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Appuyez pour conso/paramètres."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Limite de données 2G-3G atteinte"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Limite de données 4G atteinte"</string>
diff --git a/core/res/res/values-gl-rES/strings.xml b/core/res/res/values-gl-rES/strings.xml
index f842df2..a5c3c6e 100644
--- a/core/res/res/values-gl-rES/strings.xml
+++ b/core/res/res/values-gl-rES/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opcións de teléfono"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Bloqueo da pantalla"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Apagar"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emerxencia"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Informe de erros"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Crear informe de erros"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Este informe recompilará información acerca do estado actual do teu dispositivo para enviala en forma de mensaxe de correo electrónico. O informe de erros tardará un pouco en completarse desde o seu inicio ata que estea preparado para enviarse, polo que che recomendamos que teñas paciencia."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Unidade USB de <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"almacenamento USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Editar"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Alerta de uso de datos"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Toca para uso e configuración."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Límite de datos de 2G-3G acadado"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Límite de datos de 4G acadado"</string>
diff --git a/core/res/res/values-gu-rIN/strings.xml b/core/res/res/values-gu-rIN/strings.xml
index 45038b0..cd3e7be 100644
--- a/core/res/res/values-gu-rIN/strings.xml
+++ b/core/res/res/values-gu-rIN/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ફોન વિકલ્પો"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"સ્ક્રીન લૉક"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"પાવર બંધ"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"કટોકટી"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"બગ રિપોર્ટ"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"બગ રિપોર્ટ લો"</string>
     <string name="bugreport_message" msgid="398447048750350456">"આ, એક ઇ-મેઇલ સંદેશ તરીકે મોકલવા માટે, તમારા વર્તમાન ઉપકરણ સ્થિતિ વિશેની માહિતી એકત્રિત કરશે. એક બગ રિપોર્ટ પ્રારંભ કરીને તે મોકલવા માટે તૈયાર ન થઈ જાય ત્યાં સુધી તેમાં થોડો સમય લાગશે; કૃપા કરીને ધીરજ રાખો."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB ડ્રાઇવ"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB સંગ્રહ"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"સંપાદિત કરો"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"ડેટા વપરાશ ચેતવણી"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"વપરાશ અને સેટિંગ્સ જોવા ટૅપ કરો."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G ડેટા મર્યાદા પર પહોંચ્યાં"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G ડેટા મર્યાદા સુધી પહોંચ્યાં"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 8a651bd..c8b3256 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"फ़ोन विकल्‍प"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"स्‍क्रीन लॉक"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"पावर बंद"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"आपातकाल"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"बग रिपोर्ट"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"बग रिपोर्ट प्राप्त करें"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ईमेल संदेश के रूप में भेजने के लिए, इसके द्वारा आपके डिवाइस की वर्तमान स्थिति के बारे में जानकारी एकत्र की जाएगी. बग रिपोर्ट प्रारंभ करने से लेकर भेजने के लिए तैयार होने तक कुछ समय लगेगा; कृपया धैर्य रखें."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB डिस्‍क"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB मेमोरी"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"संपादित करें"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"डेटा उपयोग की सूचना"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"उपयोग व सेटिंग देखने हेतु टैप करें."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G डेटा सीमा पूर्ण हो गई"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G डेटा सीमा पूर्ण हो गई"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 16a304f..3fc3346 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -216,8 +216,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opcije telefona"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Zaključavanje zaslona"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Isključi"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Hitno"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Izvješće o bugovima"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Izvješće o programskoj pogrešci"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Time će se prikupiti podaci o trenutačnom stanju vašeg uređaja koje ćete nam poslati u e-poruci. Za pripremu izvješća o programskoj pogrešci potrebno je nešto vremena pa vas molimo za strpljenje."</string>
@@ -1356,8 +1355,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB pogon"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB pohrana"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Uredi"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Upozorenje o upotrebi podataka"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Dodirnite za upotrebu i postavke"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Dost. ogr. 2G–3G prijenosa pod."</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Dost. ogr. 4G prijenosa podataka"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index cbb72a7..825672b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefonbeállítások"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Képernyő lezárása"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Kikapcsolás"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Vészhívás"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Programhiba bejelentése"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Hibajelentés készítése"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Ezzel információt fog gyűjteni az eszköz jelenlegi állapotáról, amelyet a rendszer e-mailben fog elküldeni. Kérjük, legyen türelemmel, amíg a hibajelentés elkészül, és küldhető állapotba kerül."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB-meghajtó"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB-tár"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Szerkesztés"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Adathasználati értesítés"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Koppintson az adatokért."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-/3G-adatkorlát elérve"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G-adatkorlát elérve"</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index ae83552a..83f8995 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Հեռախոսի ընտրանքներ"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Էկրանի փական"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Անջատել"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Արտակարգ իրավիճակ"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Վրիպակի զեկույց"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Գրել սխալի զեկույց"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Սա տեղեկություններ կհավաքագրի ձեր սարքի առկա կարգավիճակի մասին և կուղարկի այն էլեկտրոնային նամակով: Որոշակի ժամանակ կպահանջվի վրիպակի մասին զեկուցելու պահից սկսած մինչ ուղարկելը: Խնդրում ենք փոքր-ինչ համբերատար լինել:"</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"USB սարքավար <xliff:g id="MANUFACTURER">%s</xliff:g>-ից"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB կրիչ"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Խմբագրել"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Տվյալների օգտագործման զգուշացում"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Հպեք և տեսեք օգտագործումը և կարգավորումները:"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G տվյալների սահմանաչափը սպառվել է"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G տվյալների սահմանաչափը սպառվել է"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index b6f6c4d..d655477 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opsi telepon"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Kunci layar"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Matikan daya"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Darurat"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Laporan bug"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Ambil laporan bug"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Ini akan mengumpulkan informasi status perangkat Anda saat ini, untuk dikirimkan sebagai pesan email. Harap bersabar, mungkin perlu waktu untuk memulai laporan bug hingga siap dikirim."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Drive USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Penyimpanan USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Edit"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Lansiran penggunaan data"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Ketuk untuk lihat penggunaan &amp; setelan."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Batas data 2G-3G terlampaui"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Batas data 4G terlampaui"</string>
diff --git a/core/res/res/values-is-rIS/strings.xml b/core/res/res/values-is-rIS/strings.xml
index 5cce18b..cd41f51 100644
--- a/core/res/res/values-is-rIS/strings.xml
+++ b/core/res/res/values-is-rIS/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Valkostir síma"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Skjálás"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Slökkva"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Neyðarsímtal"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Villutilkynning"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Útbúa villutilkynningu"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Þetta safnar upplýsingum um núverandi stöðu tækisins til að senda með tölvupósti. Það tekur smástund frá því villutilkynningin er ræst og þar til hún er tilbúin til sendingar – sýndu biðlund."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"USB-drif frá <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB-geymsla"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Breyta"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Viðvörun um gagnanotkun"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Ýttu fyrir uppl. og stillingar"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Gagnahámarki 2G og 3G náð"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Gagnahámarki 4G náð"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 13c17ce..ec7f8f9 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opzioni telefono"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Blocco schermo"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Spegni"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergenza"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Segnalazione di bug"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Apri segnalazione bug"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Verranno raccolte informazioni sullo stato corrente del dispositivo che saranno inviate sotto forma di messaggio email. Passerà un po\' di tempo prima che la segnalazione di bug aperta sia pronta per essere inviata; ti preghiamo di avere pazienza."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Unità USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Archivio USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Modifica"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Avviso sull\'utilizzo dei dati"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Tocca per uso e impostazioni."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Limite di dati 2G-3G raggiunto"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Limite di dati 4G raggiunto"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 87ec61a..290a31f 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -218,8 +218,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"אפשרויות טלפון"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"נעילת מסך"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"כיבוי"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"חירום"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"דיווח על באג"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"שלח דיווח על באג"</string>
     <string name="bugreport_message" msgid="398447048750350456">"פעולה זו תאסוף מידע על מצב המכשיר הנוכחי שלך על מנת לשלוח אותו כהודעת אימייל. היא תימשך זמן קצר מרגע פתיחת דיווח הבאג ועד לשליחת ההודעה בפועל. אנא המתן בסבלנות."</string>
@@ -1382,8 +1381,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"‏כונן USB של <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"‏אחסון USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"ערוך"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"התראה לשימוש בנתונים"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"הקש כדי להציג נתוני שימוש והגדרות."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"‏הגעת למגבלת הנתונים של 2G-3G"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"‏הגעת למגבלת הנתונים של 4G"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 213c064..5305b1f 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"携帯電話オプション"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"画面ロック"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"電源を切る"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"緊急通報"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"バグレポート"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"バグレポートを取得"</string>
     <string name="bugreport_message" msgid="398447048750350456">"現在の端末の状態に関する情報が収集され、その内容がメールで送信されます。バグレポートが開始してから送信可能な状態となるまでには多少の時間がかかりますのでご了承ください。"</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g>製USBドライブ"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USBストレージ"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"編集"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"データ使用量に関する通知"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"タップして使用状況と設定を表示します。"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G~3Gデータの上限に達しました"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4Gデータの上限に達しました"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 315ff64..1e34347 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ტელეფონის პარამეტრები"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"ეკრანის დაბლოკვა"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"კვების გამორთვა"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"საგანგებო სამსახურები"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"ხარვეზის შესახებ ანგარიში"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"შექმენით შეცდომის ანგარიში"</string>
     <string name="bugreport_message" msgid="398447048750350456">"იგი შეაგროვებს ინფორმაციას თქვენი მოწყობილობის ამჟამინდელი მდგომარეობის შესახებ, რათა ის ელფოსტის შეტყობინების სახით გააგზავნოს. ხარვეზის ანგარიშის მომზადებასა და შეტყობინების გაგზავნას გარკვეული დრო სჭირდება. გთხოვთ, მოითმინოთ."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB დისკი"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB მეხსიერება"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"რედაქტირება"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"მონაცემთა მოხმარების გაფრთხილება"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"შეეხეთ მოხმარებისა და პარამეტრების სანახავად."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G მონაცემთა ლიმიტი ამოიწურა"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G მონაცემთა ლიმიტი ამოიწურა"</string>
diff --git a/core/res/res/values-kk-rKZ/strings.xml b/core/res/res/values-kk-rKZ/strings.xml
index 525fbee..9a227c4 100644
--- a/core/res/res/values-kk-rKZ/strings.xml
+++ b/core/res/res/values-kk-rKZ/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Телефон опциялары"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Экранды құлыптау"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Өшіру"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Төтенше жағдай"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Вирус туралы хабарлау"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Қате туралы есеп құру"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Құрылғының қазіргі күйі туралы ақпаратты жинап, электрондық хабармен жібереді. Есеп әзір болғанша біраз уақыт кетеді, шыдай тұрыңыз."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB дискі"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB жады"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Өзгерту"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Деректер трафигі туралы ескерту"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Трафик пен параметрлерді көру үшін түртіңіз."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G деректер шегіне жеттіңіз"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G деректер шегіне жеттіңіз"</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index 014d844..4eaadfe 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ជម្រើស​ទូរស័ព្ទ"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"ចាក់​សោ​អេក្រង់"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"បិទ"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"អាសន្ន"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"របាយការណ៍​កំហុស"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"យក​របាយការណ៍​កំហុស"</string>
     <string name="bugreport_message" msgid="398447048750350456">"វា​នឹង​​ប្រមូល​ព័ត៌មាន​អំពី​ស្ថានភាព​ឧបករណ៍​របស់​អ្នក ដើម្បី​ផ្ញើ​ជា​សារ​អ៊ីមែល។ វា​នឹង​ចំណាយ​ពេល​តិច​ពី​ពេល​ចាប់ផ្ដើម​របាយការណ៍​រហូត​ដល់​ពេល​វា​រួចរាល់​ដើម្បី​ផ្ញើ សូម​អត់ធ្មត់។"</string>
@@ -1332,8 +1331,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"ឧបករណ៍ផ្ទុក USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"ឧបករណ៍​ផ្ទុក​យូអេសប៊ី"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល​"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"ការដាស់តឿនពីការប្រើទិន្នន័យ"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"ប៉ះដើម្បីមើលការប្រើប្រាស់ និងការកំណត់"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"បាន​ដល់​ដែន​កំណត់​ទិន្នន័យ 2G-3G"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"បាន​ដល់​ដែន​កំណត់​ទិន្នន័យ 4G"</string>
diff --git a/core/res/res/values-kn-rIN/strings.xml b/core/res/res/values-kn-rIN/strings.xml
index ab60289..0a9a511 100644
--- a/core/res/res/values-kn-rIN/strings.xml
+++ b/core/res/res/values-kn-rIN/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ಫೋನ್ ಆಯ್ಕೆಗಳು"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"ಸ್ಕ್ರೀನ್ ಲಾಕ್"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"ಪವರ್ ಆಫ್ ಮಾಡು"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"ತುರ್ತು"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"ದೋಷದ ವರದಿ"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"ದೋಷ ವರದಿ ರಚಿಸಿ"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ನಿಮ್ಮ ಸಾಧನದ ಪ್ರಸ್ತುತ ಸ್ಥಿತಿಯ ಕುರಿತು ಮಾಹಿತಿಯನ್ನು ಸಂಗ್ರಹಿಸಿಕೊಳ್ಳುವುದರ ಜೊತೆ ಇ-ಮೇಲ್ ರೂಪದಲ್ಲಿ ನಿಮಗೆ ರವಾನಿಸುತ್ತದೆ. ಇದು ದೋಷ ವರದಿಯನ್ನು ಪ್ರಾರಂಭಿಸಿದ ಸಮಯದಿಂದ ಅದನ್ನು ಕಳುಹಿಸುವವರೆಗೆ ಸ್ವಲ್ಪ ಸಮಯವನ್ನು ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ; ದಯವಿಟ್ಟು ತಾಳ್ಮೆಯಿಂದಿರಿ."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB ಡ್ರೈವ್"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB ಸಂಗ್ರಹಣೆ"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"ಎಡಿಟ್"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"ಡೇಟಾ ಬಳಕೆ ಎಚ್ಚರಿಕೆ"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"ಬಳಕೆ ಮತ್ತು ಸೆಟ್ಟಿಂಗ್‍ಗಳನ್ನು ವೀಕ್ಷಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G ಡೇಟಾ ಮೀತಿಯನ್ನು ತಲುಪಿದೆ"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G ಡೇಟಾ ಮೀತಿಯನ್ನು ತಲುಪಿದೆ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 5d226a6..4191865 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"휴대전화 옵션"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"화면 잠금"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"종료"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"긴급 전화"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"버그 신고"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"버그 신고"</string>
     <string name="bugreport_message" msgid="398447048750350456">"현재 기기 상태에 대한 정보를 수집하여 이메일 메시지로 전송합니다. 버그 신고를 시작하여 전송할 준비가 되려면 약간 시간이 걸립니다."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB 드라이브"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB 저장소"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"수정"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"데이터 사용 알림"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"사용량 및 설정을 보려면 탭하세요."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G 데이터 한도에 도달함"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G 데이터 한도에 도달함"</string>
diff --git a/core/res/res/values-ky-rKG/strings.xml b/core/res/res/values-ky-rKG/strings.xml
index 99bba3a7..1a5f348 100644
--- a/core/res/res/values-ky-rKG/strings.xml
+++ b/core/res/res/values-ky-rKG/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Телефон мүмкүнчүлүктөрү"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Экран кулпусу"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Кубатын өчүрүү"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Тез жардам"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Ката тууралуу билдирүү"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Ката тууралуу билдирүү түзүү"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Бул сиздин түзмөгүңүздүн учурдагы абалын эмейл билдирүүсү катары жөнөтүш максатында маалымат чогултат. Ката тууралуу билдирүү түзүлүп башталып, жөнөтүлгөнгө чейин бир аз убакыт керек болот; сураныч, бир аз күтө туруңуз."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB түзмөгү"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB эстутуму"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Өзгөртүү"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Дайындарды колдонуу айгайы"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Колдонулушун жана жөндөөлөрүн көрүү үчүн таптаңыз."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G дайындар чегине жетти"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G дайындар чегине жетти"</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index 9762f26..26c9053 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ໂຕເລືອກໂທລະສັບ"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"ລັອກໜ້າຈໍ"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"ປິດ"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"ສຸກເສີນ"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"ລາຍງານຂໍ້ຜິດພາດ"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"ໃຊ້ລາຍງານຂໍ້ບົກພ່ອງ"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ນີ້ຈະເປັນການເກັບກຳຂໍ້ມູນກ່ຽວກັບ ສະຖານະປັດຈຸບັນຂອງອຸປະກອນທ່ານ ເພື່ອສົ່ງເປັນຂໍ້ຄວາມທາງອີເມວ. ມັນຈະໃຊ້ເວລາໜ້ອຍນຶ່ງ ໃນການເລີ່ມຕົ້ນການລາຍງານຂໍ້ຜິດພາດ ຈົນກວ່າຈະພ້ອມທີ່ຈະສົ່ງໄດ້, ກະລຸນາລໍຖ້າ."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB ດ​ຣ້າຍ"</string>
     <string name="storage_usb" msgid="3017954059538517278">"ບ່ອນຈັດເກັບຂໍ້ມູນ USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"ແກ້ໄຂ"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"ແຈ້ງເຕືອນການໃຊ້ອິນເຕີເນັດ"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"ແຕະເພື່ອເບິ່ງການນຳໃຊ້ ແລະ ການຕັ້ງຄ່າ."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"ໃຊ້​ຂໍ້​ມູນ 2G-3G ຮອດ​ຈຳ​ນວນ​ທີ່​ຈຳ​ກັດ​ແລ້ວ"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"ໃຊ້​ຂໍ້​ມູນ 4G ຮອດ​ຈຳ​ນວນ​ທີ່​ຈຳ​ກັດ​ແລ້ວ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 6446066..a2f5eb8 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -218,8 +218,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefono parinktys"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Ekrano užraktas"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Išjungiamas maitinimas"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Skambutis pagalbos numeriu"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Pranešimas apie riktą"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Pranešti apie riktą"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Bus surinkta informacija apie dabartinę įrenginio būseną ir išsiųsta el. pašto pranešimu. Šiek tiek užtruks, kol pranešimas apie riktą bus paruoštas siųsti; būkite kantrūs."</string>
@@ -1382,8 +1381,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"„<xliff:g id="MANUFACTURER">%s</xliff:g>“ atmintukas"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB atmintis"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Redaguoti"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Duomenų naudojimo įspėjimas"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Pal. ir perž. naud. i. bei nust."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Pasiektas 2G–3G duomenų apribojimas"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Pasiektas 4G duomenų apribojimas"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index b0e7af5..6ea7d6b 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -216,8 +216,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Tālruņa opcijas"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Ekrāna bloķētājs"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Strāvas padeve ir izslēgta."</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Ārkārtas"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Kļūdu ziņojums"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Kļūdu ziņojuma sagatavošana"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Veicot šo darbību, tiks apkopota informācija par jūsu ierīces pašreizējo stāvokli un nosūtīta e-pasta ziņojuma veidā. Kļūdu ziņojuma pabeigšanai un nosūtīšanai var būt nepieciešams laiks. Lūdzu, esiet pacietīgs."</string>
@@ -1356,8 +1355,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB disks"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB atmiņa"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Rediģēt"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Brīdinājums par datu lietojumu"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Piesk., lai sk. lietoj. un iest."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Sasniegts 2G-3G datu ierobež."</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Sasniegts 4G datu ierobežojums"</string>
diff --git a/core/res/res/values-mcc238-mnc06/config.xml b/core/res/res/values-mcc238-mnc06/config.xml
deleted file mode 100644
index afc0cc4..0000000
--- a/core/res/res/values-mcc238-mnc06/config.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2014, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <!-- SIM does not save, but the voice mail number to be changed. -->
-    <bool name="editable_voicemailnumber">true</bool>
-</resources>
diff --git a/core/res/res/values-mk-rMK/strings.xml b/core/res/res/values-mk-rMK/strings.xml
index a47a483..3f228203 100644
--- a/core/res/res/values-mk-rMK/strings.xml
+++ b/core/res/res/values-mk-rMK/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Опции на телефон"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Заклучи екран"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Исклучи"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Итен случај"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Извештај за грешка"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Земи извештај за грешки"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Ова ќе собира информации за моменталната состојба на вашиот уред, за да ги испрати како порака по е-пошта. Тоа ќе одземе малку време почнувајќи од извештајот за грешки додека не се подготви за праќање; бидете трпеливи."</string>
@@ -1332,8 +1331,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> УСБ-меморија"</string>
     <string name="storage_usb" msgid="3017954059538517278">"УСБ меморија"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Уреди"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Известување за потрошен сообраќај"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Допрете за употреба и поставки."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Постигна лимит за 2G-3G податоци"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Постигнат лимит за 4G податоци"</string>
diff --git a/core/res/res/values-ml-rIN/strings.xml b/core/res/res/values-ml-rIN/strings.xml
index eaa8deb..beb6a67 100644
--- a/core/res/res/values-ml-rIN/strings.xml
+++ b/core/res/res/values-ml-rIN/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ഫോൺ ഓപ്‌ഷനുകൾ"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"സ്‌ക്രീൻ ലോക്ക്"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"പവർ ഓഫാക്കുക"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"അടിയന്തിരാവശ്യം"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"ബഗ് റിപ്പോർട്ട്"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"ബഗ് റിപ്പോർട്ട് എടുക്കുക"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ഒരു ഇമെയിൽ സന്ദേശമായി അയയ്‌ക്കുന്നതിന്, ഇത് നിങ്ങളുടെ നിലവിലെ ഉപകരണ നിലയെക്കുറിച്ചുള്ള വിവരങ്ങൾ ശേഖരിക്കും. ബഗ് റിപ്പോർട്ട് ആരംഭിക്കുന്നതിൽ നിന്ന് ഇത് അയയ്‌ക്കാനായി തയ്യാറാകുന്നതുവരെ അൽപ്പസമയമെടുക്കും; ക്ഷമയോടെ കാത്തിരിക്കുക."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB ഡ്രൈവ്"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB സ്റ്റോറേജ്"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"എഡിറ്റുചെയ്യുക"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"ഡാറ്റാ ഉപയോഗ മുന്നറിയിപ്പ്"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"ഉപയോഗവും ക്രമീകരണവും കാണാൻ ടാപ്പുചെയ്യുക."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G ഡാറ്റ പരിധിയിലെത്തി"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G ഡാറ്റ പരിധിയിലെത്തി"</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index 79dbc68..bd80f84 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Утасны сонголтууд"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Дэлгэцний түгжээ"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Унтраах"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Яаралтай тусламж"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Алдаа мэдээллэх"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Согог репорт авах"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Энэ таны төхөөрөмжийн одоогийн статусын талаарх мэдээллийг цуглуулах ба имэйл мессеж болгон илгээнэ. Алдааны мэдэгдлээс эхэлж илгээхэд бэлэн болоход хэсэг хугацаа зарцуулагдана тэвчээртэй байна уу."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB диск"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB сан"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Засах"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Дата ашиглалтын сануулга"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Хэрэглээ, тохиргоог харах бол товш."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G дата хязгаарт хүрсэн"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G дата хязгаарт хүрсэн"</string>
diff --git a/core/res/res/values-mr-rIN/strings.xml b/core/res/res/values-mr-rIN/strings.xml
index fce64c4..f0e437a 100644
--- a/core/res/res/values-mr-rIN/strings.xml
+++ b/core/res/res/values-mr-rIN/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"फोन पर्याय"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"स्क्रीन लॉक"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"बंद"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"आणीबाणी"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"दोष अहवाल"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"दोष अहवाल घ्या"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ई-मेल संदेश म्हणून पाठविण्यासाठी, हे आपल्या वर्तमान डिव्हाइस स्थितीविषयी माहिती संकलित करेल. दोष अहवाल प्रारंभ करण्यापासून तो पाठविण्यापर्यंत थोडा वेळ लागेल; कृपया धीर धरा."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB ड्राइव्‍ह"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB संचयन"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"संपादित करा"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"डेटा वापर सूचना"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"वापर आणि सेटिंग्ज पाहण्यासाठी टॅप करा."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G डेटा मर्यादा गाठली"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G डेटा मर्यादा गाठली"</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index 53181ce..60330e5 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Pilihan telefon"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Kunci skrin"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Matikan kuasa"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Kecemasan"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Laporan pepijat"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Ambil laporan pepijat"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Ini akan mengumpul maklumat tentang keadaan peranti semasa anda untuk dihantarkan sebagai mesej e-mel. Harap bersabar, mungkin perlu sedikit masa untuk memulakan laporan sehingga siap untuk dihantar."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Pemacu USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Storan USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Edit"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Makluman penggunaan data"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Ketik utk lihat p\'gunaan &amp; ttpn."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Mencapai had data 2G-3G"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Mencapai had data 4G"</string>
diff --git a/core/res/res/values-my-rMM/strings.xml b/core/res/res/values-my-rMM/strings.xml
index 987d764..c18ff7f 100644
--- a/core/res/res/values-my-rMM/strings.xml
+++ b/core/res/res/values-my-rMM/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ဖုန်းဆိုင်ရာရွေးချယ်မှုများ"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"ဖုန်းမျက်နှာပြင်အား သော့ချရန်"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"ပါဝါပိတ်ရန်"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"အရေးပေါ်"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"အမှားရှာဖွေပြင်ဆင်မှုမှတ်တမ်း"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"အမှားရှာဖွေပြင်ဆင်မှုမှတ်တမ်းအား ယူရန်"</string>
     <string name="bugreport_message" msgid="398447048750350456">"သင့်ရဲ့ လက်ရှိ စက်အခြေအနေ အချက်အလက်များကို အီးမေးလ် အနေဖြင့် ပေးပို့ရန် စုဆောင်းပါမည်။ အမှားရှာဖွေပြင်ဆင်မှုမှတ်တမ်းမှ ပေးပို့ရန် အသင့်ဖြစ်သည်အထိ အချိန် အနည်းငယ်ကြာမြင့်မှာ ဖြစ်သဖြင့် သည်းခံပြီး စောင့်ပါရန်"</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB ဒရိုက်ဗ်"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USBဖြင့် သိမ်းဆည်း"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"ပြင်ဆင်ရန်"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"ဒေတာအသုံးပြုမှုသတိပေးချက်"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"အသုံးပြုမှုနှင့် ဆက်တင်များကိုကြည့်ရန် တို့ပါ။"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G ဒေတာ ကန့်သတ်ချက် ပြည့်မီသွားပြီ"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G ဒေတာ ကန့်သတ်ချက် ပြည့်မီသွားပြီ"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index c807c14..9ff7803 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefoninnstillinger"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Lås skjermen"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Slå av"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Nødssituasjon"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Feilrapport"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Utfør feilrapport"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Informasjon om tilstanden til enheten din samles inn og sendes som en e-post. Det tar litt tid fra du starter feilrapporten til e-posten er klar, så vær tålmodig."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB-stasjon"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB-lagring"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Rediger"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Varsel om databruk"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Trykk for å se bruken og innstillingene."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Datagrensen for 2G-3G er nådd"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Datagrensen for 4G er nådd"</string>
diff --git a/core/res/res/values-ne-rNP/strings.xml b/core/res/res/values-ne-rNP/strings.xml
index 65d3b69..e039927 100644
--- a/core/res/res/values-ne-rNP/strings.xml
+++ b/core/res/res/values-ne-rNP/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"फोन विकल्पहरू"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"स्क्रिन बन्द"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"बन्द गर्नुहोस्"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"आपतकालीन"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"बग रिपोर्ट"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"बग रिपोर्ट लिनुहोस्"</string>
     <string name="bugreport_message" msgid="398447048750350456">"एउटा इमेल सन्देशको रूपमा पठाउनलाई यसले तपाईँको हालैको उपकरणको अवस्थाको बारेमा सूचना जम्मा गर्ने छ। बग रिपोर्ट सुरु गरेदेखि पठाउन तयार नभएसम्म यसले केही समय लिन्छ; कृपया धैर्य गर्नुहोस्।"</string>
@@ -1336,8 +1335,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB ड्राइभ"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB भण्डारण"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"सम्पादन गर्नुहोस्"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"डेटा प्रयोग बारे सतर्कता"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"प्रयोग र सेटिङहरू हेर्न ट्याप गर्नुहोस्।"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G डेटा सीमा पुग्यो"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G डेटा सीमा पुग्यो"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 69a4074..8e42bbc 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefoonopties"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Schermvergrendeling"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Uitschakelen"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Noodgeval"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Foutenrapport"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Foutenrapport genereren"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Hiermee worden gegevens over de huidige status van je apparaat verzameld en als e-mail verzonden. Wanneer u een foutenrapport start, duurt het even voordat het kan worden verzonden. Even geduld alstublieft."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB-drive"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB-opslag"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Bewerken"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Melding voor datagebruik"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Tik voor gebruik en instellingen"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Gegevenslimiet van 2G-3G bereikt"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Gegevenslimiet van 4G bereikt"</string>
diff --git a/core/res/res/values-pa-rIN/strings.xml b/core/res/res/values-pa-rIN/strings.xml
index ac80d51..d3476aa 100644
--- a/core/res/res/values-pa-rIN/strings.xml
+++ b/core/res/res/values-pa-rIN/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ਫੋਨ ਚੋਣਾਂ"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"ਸਕ੍ਰੀਨ ਲੌਕ"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"ਪਾਵਰ ਬੰਦ"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"ਸੰਕਟਕਾਲ"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"ਬਗ ਰਿਪੋਰਟ"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"ਬਗ ਰਿਪੋਰਟ ਲਓ"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ਇਹ ਇੱਕ ਈ-ਮੇਲ ਸੁਨੇਹਾ ਭੇਜਣ ਲਈ, ਤੁਹਾਡੀ ਵਰਤਮਾਨ ਡੀਵਾਈਸ ਬਾਰੇ ਜਾਣਕਾਰੀ ਇਕੱਤਰ ਕਰੇਗਾ। ਬਗ ਰਿਪੋਰਟ ਸ਼ੁਰੂ ਕਰਨ ਵਿੱਚ ਥੋੜ੍ਹਾ ਸਮਾਂ ਲੱਗੇਗਾ ਜਦੋਂ ਤੱਕ ਇਹ ਭੇਜੇ ਜਾਣ ਲਈ ਤਿਆਰ ਨਾ ਹੋਵੇ, ਕਿਰਪਾ ਕਰਕੇ ਧੀਰਜ ਰੱਖੋ।"</string>
@@ -364,7 +363,7 @@
     <string name="permdesc_accessCoarseLocation" msgid="2538200184373302295">"ਐਪ ਨੂੰ ਤੁਹਾਡਾ ਅਨੁਮਾਨਿਤ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਪ੍ਰਾਪਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਨੈੱਟਵਰਕ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਸਰੋਤ ਵਰਤਦੇ ਹੋਏ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਸੇਵਾਵਾਂ ਰਾਹੀਂ ਪ੍ਰਾਪਤ ਕੀਤਾ ਜਾਂਦਾ ਹੈ ਜਿਵੇਂ ਸੈਲ ਟਾਵਰ ਅਤੇ Wi-Fi. ਇਹ ਨਿਰਧਾਰਿਤ ਸਥਾਨ ਸੇਵਾਵਾਂ ਚਾਲੂ ਅਤੇ ਐਪ ਨੂੰ ਉਹਨਾਂ ਨੂੰ ਵਰਤਣ ਲਈ ਤੁਹਾਡੀ ਡੀਵਾਈਸ ਤੇ ਹੋਣੀਆਂ ਚਾਹੀਦੀਆਂ ਹਨ। ਐਪਸ ਇਸਦੀ ਵਰਤੋਂ ਇਹ ਅਨੁਮਾਨ ਲਗਾਉਣ ਲਈ ਕਰ ਸਕਦੇ ਹਨ ਕਿ ਤੁਸੀਂ ਕਿੱਥੇ ਹੋ।"</string>
     <string name="permlab_modifyAudioSettings" msgid="6095859937069146086">"ਆਪਣੀਆਂ ਔਡੀਓ ਸੈਟਿੰਗਾਂ ਬਦਲੋ"</string>
     <string name="permdesc_modifyAudioSettings" msgid="3522565366806248517">"ਔਪ ਨੂੰ ਗਲੋਬਲ ਔਡੀਓ ਸੈਟਿੰਗਾਂ ਸੰਸ਼ੋਧਿਤ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ ਜਿਵੇਂ ਵੌਲਿਊਮ ਅਤੇ ਆਊਟਪੁਟ ਲਈ ਕਿਹੜਾ ਸਪੀਕਰ ਵਰਤਿਆ ਜਾਂਦਾ ਹੈ।"</string>
-    <string name="permlab_recordAudio" msgid="3876049771427466323">"ਔਡੀਓ ਰਿਕਾਰਡ ਕਰੋ"</string>
+    <string name="permlab_recordAudio" msgid="3876049771427466323">"ਔਡੀਓ ਰਿਕਾਰਡ ਕਰਨ"</string>
     <string name="permdesc_recordAudio" msgid="4906839301087980680">"ਐਪ ਨੂੰ ਮਾਈਕ੍ਰੋਫੋਨ ਨਾਲ ਔਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਅਨੁਮਤੀ ਐਪ ਨੂੰ ਤੁਹਾਡੀ ਪੁਸ਼ਟੀ ਤੋਂ ਬਿਨਾਂ ਕਿਸੇ ਵੀ ਸਮੇਂ ਔਡੀਓ ਰਿਕਾਰਡ ਕਰਨ ਦੀ ਆਗਿਆ ਦਿੰਦੀ ਹੈ।"</string>
     <string name="permlab_sim_communication" msgid="2935852302216852065">"SIM ਨੂੰ ਕਮਾਂਡਾਂ ਭੇਜੋ"</string>
     <string name="permdesc_sim_communication" msgid="5725159654279639498">"ਐਪ ਨੂੰ SIM ਨੂੰ ਕਮਾਂਡਾਂ ਭੇਜਣ ਦੀ ਆਗਿਆ ਦਿੰਦਾ ਹੈ। ਇਹ ਬਹੁਤ ਘਾਤਕ ਹੈ।"</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB ਡ੍ਰਾਇਵ"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB ਸਟੋਰੇਜ"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"ਸੰਪਾਦਿਤ ਕਰੋ"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"ਡੈਟਾ ਵਰਤੋਂ ਚੇਤਾਵਨੀ"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"ਵਰਤੋਂ ਅਤੇ ਸੈਟਿੰਗਾਂ ਨੂੰ ਵੇਖਣ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G ਡੈਟਾ ਸੀਮਾ ਪੂਰੀ ਹੋ ਗਈ"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G ਡੈਟਾ ਸੀਮਾ ਪੂਰੀ ਹੋਈ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 8698846..a7571ef 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -218,8 +218,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opcje telefonu"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Blokada ekranu"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Wyłącz"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Alarmowy"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Zgłoszenie błędu"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Zgłoś błąd"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Informacje o bieżącym stanie urządzenia zostaną zebrane i wysłane e-mailem. Przygotowanie zgłoszenia błędu do wysłania chwilę potrwa, więc zachowaj cierpliwość."</string>
@@ -1382,8 +1381,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Dysk USB (<xliff:g id="MANUFACTURER">%s</xliff:g>)"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Nośnik USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Edytuj"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Alert transmisji danych"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Kliknij, by wyświetlić użycie i ustawienia."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Osiągnięto limit danych 2G/3G"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Osiągnięto limit danych 4G"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 74b7519..24265ee 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opções do telefone"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Bloquear tela"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Desligar"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergência"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de bugs"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Obter relatório de bugs"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Isto coletará informações sobre o estado atual do dispositivo para enviá-las em uma mensagem de e-mail. Após iniciar o relatório de bugs, será necessário aguardar algum tempo até que esteja pronto para ser enviado."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Drive USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Armazenamento USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Editar"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Alerta de uso de dados"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Toque para ver uso e config."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Limite de dados 2G-3G atingido"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Limite de dados 4G atingido"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 58e4097..6270210 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opções do telefone"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Bloqueio de ecrã"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Desligar"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergência"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de erros"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Criar relatório de erros"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Será recolhida informação sobre o estado atual do seu dispositivo a enviar através de uma mensagem de email. Demorará algum tempo até que o relatório de erro esteja pronto para ser enviado. Aguarde um pouco."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Unidade USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Armazenamento USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Editar"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Alerta de utilização de dados"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Toque para ver a utilização e definições"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Limite de dados 2G/3G atingido"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Limite de dados 4G atingido"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 74b7519..24265ee 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opções do telefone"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Bloquear tela"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Desligar"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergência"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Relatório de bugs"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Obter relatório de bugs"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Isto coletará informações sobre o estado atual do dispositivo para enviá-las em uma mensagem de e-mail. Após iniciar o relatório de bugs, será necessário aguardar algum tempo até que esteja pronto para ser enviado."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Drive USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Armazenamento USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Editar"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Alerta de uso de dados"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Toque para ver uso e config."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Limite de dados 2G-3G atingido"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Limite de dados 4G atingido"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 5011363..29c0a59 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -216,8 +216,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opțiuni telefon"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Blocați ecranul"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Opriți alimentarea"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Urgență"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Raport despre erori"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Executați un raport despre erori"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Acest raport va colecta informații despre starea actuală a dispozitivului, pentru a le trimite într-un e-mail. Aveți răbdare după pornirea raportului despre erori până când va fi gata de trimis."</string>
@@ -1356,8 +1355,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Unitate USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Dsipozitiv de stocare USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Editați"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Alertă pentru utilizarea datelor"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Atingeți ca să vedeți utilizarea/setările."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Ați atins limita de date 2G-3G"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Ați atins limita de date 4G"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 7850cd8..ed6c798 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -218,8 +218,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Параметры телефона"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Блокировка экрана"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Отключить питание"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Экстренный вызов"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Отчет об ошибке"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Отчет об ошибке"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Информация о текущем состоянии вашего устройства будет собрана и отправлена по электронной почте. Подготовка отчета займет некоторое время."</string>
@@ -1382,8 +1381,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"USB-накопитель <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB-накопитель"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Изменить"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Лимит на передачу данных"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Нажмите, чтобы проверить трафик и настройки."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Достигнут лимит трафика 2G/3G"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Достигнут лимит трафика 4G"</string>
diff --git a/core/res/res/values-si-rLK/strings.xml b/core/res/res/values-si-rLK/strings.xml
index 7cab897..91d5bbe 100644
--- a/core/res/res/values-si-rLK/strings.xml
+++ b/core/res/res/values-si-rLK/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"දුරකථන විකල්ප"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"තිර අගුල"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"බලය අක්‍රිය කරන්න"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"හදිසි"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"දෝෂ වර්තාව"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"දෝෂ වාර්තාවක් ගන්න"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ඊ-තැපැල් පණිවිඩයක් ලෙස යැවීමට මෙය ඔබගේ වත්මන් උපාංග තත්වය ගැන තොරතුරු එකතු කරනු ඇත. දෝෂ වාර්තාව ආරම්භ කර එය යැවීමට සූදානම් කරන තෙක් එයට කිසියම් කාලයක් ගතවනු ඇත; කරුණාකර ඉවසන්න."</string>
@@ -1332,8 +1331,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB ධාවකය"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB ආචයනය"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"සංස්කරණය කරන්න"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"දත්ත භාවිතය ගැන ඇඟවීම"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"භාවිතය සහ සැකසීම් බැලීමට තට්ටු කරන්න."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G දත්ත සීමාවට ළඟාවී ඇත"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G දත්ත සීමාවට ළඟාවී ඇත"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 26e275f..5c129af 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -218,8 +218,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Možnosti telefónu"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Zámka obrazovky"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Vypnúť"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Tiesňové volanie"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Hlásenie o chybách"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Vytvoriť hlásenie chyby"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Týmto zhromaždíte informácie o aktuálnom stave zariadenia. Informácie je potom možné odoslať e-mailom, chvíľu však potrvá, kým bude hlásenie chyby pripravené na odoslanie. Prosíme vás preto o trpezlivosť."</string>
@@ -1382,8 +1381,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Disk USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Ukladací priestor USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Upraviť"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Upozornenie na spotrebu dát"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Klepnutím zobrazíte využitie a nastavenia."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Bol dosiahnutý limit 2G–3G"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Bol dosiahnutý limit 4G"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 7f49968..76f1e66 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -218,8 +218,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Možnosti telefona"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Zaklep zaslona"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Izklopi"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Klic v sili"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Poročilo o napakah"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Ustvari poročilo o napakah"</string>
     <string name="bugreport_message" msgid="398447048750350456">"S tem bodo zbrani podatki o trenutnem stanju naprave, ki bodo poslani v e-poštnem sporočilu. Izvedba poročila o napakah in priprava trajata nekaj časa, zato bodite potrpežljivi."</string>
@@ -1382,8 +1381,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Pogon USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Pomnilnik USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Uredi"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Opozorilo o preneseni količini podatkov"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Dot. se za ogled upor. in nast."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Dosežena pod. omejitev za 2G/3G"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Dosežena pod. omejitev za 4G"</string>
diff --git a/core/res/res/values-sq-rAL/strings.xml b/core/res/res/values-sq-rAL/strings.xml
index c906f74..0fb3ebe 100644
--- a/core/res/res/values-sq-rAL/strings.xml
+++ b/core/res/res/values-sq-rAL/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Opsionet e telefonit"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Kyçja e ekranit"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Fik"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Urgjenca"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Raporti i defekteve në kod"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Merr raportin e defekteve në kod"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Ky funksion mundëson mbledhjen e informacioneve mbi gjendjen aktuale të pajisjes për ta dërguar si mesazh mail-i. Do të duhet pak kohë nga nisja e raportit të defekteve në kod. Faleminderit për durimin."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"USB-ja nga <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Hapësira ruajtëse e USB-së"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Redakto"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Sinjalizimi i të dhënave"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Trokit për të parë përdorimin dhe cilësimet."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Kufiri i të dhënave 2G-3G u arrit"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Kufiri i të dhënave 4G u arrit"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index f029dbb..6e99a9b 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -216,8 +216,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Опције телефона"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Закључај екран"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Искључи"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Хитни позив"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Извештај о грешци"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Направи извештај о грешци"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Овим ће се прикупити информације о тренутном стању уређаја како би биле послате у поруци е-поште. Од започињања извештаја о грешци до тренутка за његово слање проћи ће неко време; будите стрпљиви."</string>
@@ -1356,8 +1355,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB диск"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB меморија"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Измени"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Обавештење о потрошњи података"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Додирните за потрошњу и подешавања."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Нема више 2G-3G података"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Нема више 4G података"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 3f4f836..66f085f 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefonalternativ"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Skärmlås"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Stäng av"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Nödsituation"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Felrapport"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Skapa felrapport"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Nu hämtas information om aktuell status för enheten, som sedan skickas i ett e-postmeddelade. Det tar en liten stund innan felrapporten är färdig och kan skickas, så vi ber dig ha tålamod."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"USB-enhet (<xliff:g id="MANUFACTURER">%s</xliff:g>)"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB-lagring"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Redigera"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Varning – dataanvändning"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Visa användning och inställning."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Datagränsen för 2G-3G har uppnåtts"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Datagränsen för 4G har uppnåtts"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 9cba390..4832546 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -212,8 +212,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Chaguo za simu"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Funga skrini"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Zima"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Dharura"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Ripoti ya hitilafu"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Chukua ripoti ya hitilafu"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Hii itakusanya maelezo kuhusu hali ya kifaa chako kwa sasa, na itume kama barua pepe. Itachukua muda mfupi tangu ripoti ya hitilafu ianze kuzalishwa hadi iwe tayari kutumwa; vumilia."</string>
@@ -1328,8 +1327,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Hifadhi ya USB iliyotengenezwa na <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Hifadhi ya USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Badilisha"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Tahadhari ya matumizi ya data"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Gonga ili uangalie matumizi na mipangilio."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Kikomo data ya 2G-3G kimefikiwa"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Kikomo cha data ya 4G kimefikiwa"</string>
diff --git a/core/res/res/values-ta-rIN/strings.xml b/core/res/res/values-ta-rIN/strings.xml
index e784a76..f6bb25c 100644
--- a/core/res/res/values-ta-rIN/strings.xml
+++ b/core/res/res/values-ta-rIN/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"தொலைபேசி விருப்பங்கள்"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"திரைப் பூட்டு"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"முடக்கு"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"அவசர அழைப்பு"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"பிழை அறிக்கை"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"பிழை அறிக்கையை எடு"</string>
     <string name="bugreport_message" msgid="398447048750350456">"உங்கள் நடப்புச் சாதன நிலையை மின்னஞ்சல் செய்தியாக அனுப்ப, அது குறித்த தகவலை இது சேகரிக்கும். பிழை அறிக்கையைத் தொடங்குவதில் இருந்து, அது அனுப்புவதற்குத் தயாராகும் வரை, இதற்குச் சிறிது நேரம் ஆகும்; பொறுமையாகக் காத்திருக்கவும்."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB டிரைவ்"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB சேமிப்பிடம்"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"திருத்து"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"தரவுப் பயன்பாடு குறித்த எச்சரிக்கை"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"தரவு உபயோகம், அமைப்புகளைப் பார்க்க, தட்டவும்."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G தரவு வரம்பைக் கடந்தது"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G தரவு வரம்பைக் கடந்தது"</string>
diff --git a/core/res/res/values-te-rIN/strings.xml b/core/res/res/values-te-rIN/strings.xml
index 21ed7c6..24bfebd 100644
--- a/core/res/res/values-te-rIN/strings.xml
+++ b/core/res/res/values-te-rIN/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ఫోన్ ఎంపికలు"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"స్క్రీన్ లాక్"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"పవర్ ఆఫ్ చేయి"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"అత్యవసరం"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"బగ్ నివేదిక"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"బగ్ నివేదికను సిద్ధం చేయి"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ఇది ఇ-మెయిల్ సందేశం రూపంలో పంపడానికి మీ ప్రస్తుత పరికర స్థితి గురించి సమాచారాన్ని సేకరిస్తుంది. బగ్ నివేదికను ప్రారంభించడం మొదలుకొని పంపడానికి సిద్ధం చేసే వరకు ఇందుకు కొంత సమయం పడుతుంది; దయచేసి ఓపిక పట్టండి."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB డ్రైవ్"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB నిల్వ"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"సవరించు"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"డేటా వినియోగ హెచ్చరిక"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"వినియోగం,సెట్టింగ్‌ల కోసం నొక్కండి"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G డేటా పరిమితిని చేరుకుంది"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G డేటా పరిమితిని చేరుకుంది"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index df9b182..818bb9f 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"ตัวเลือกโทรศัพท์"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"ล็อกหน้าจอ"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"ปิดเครื่อง"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"เหตุฉุกเฉิน"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"รายงานข้อบกพร่อง"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"ใช้รายงานข้อบกพร่อง"</string>
     <string name="bugreport_message" msgid="398447048750350456">"การดำเนินการนี้จะรวบรวมข้อมูลเกี่ยวกับสถานะปัจจุบันของอุปกรณ์ของคุณ โดยจะส่งไปในรูปแบบข้อความอีเมล อาจใช้เวลาสักครู่ตั้งแต่เริ่มการสร้างรายงานข้อบกพร่องจนกระทั่งเสร็จสมบูรณ์ โปรดอดทนรอ"</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"ไดรฟ์ USB ของ <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"ที่เก็บข้อมูล USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"แก้ไข"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"การแจ้งเตือนการใช้อินเทอร์เน็ต"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"แตะเพื่อดูการใช้งานและการตั้งค่า"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"ถึงขีดจำกัดข้อมูล 2G-3G แล้ว"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"ถึงขีดจำกัดข้อมูล 4G แล้ว"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index fac6eeb..81c5fd0 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Pagpipilian sa telepono"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Pag-lock sa screen"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"I-off"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Emergency"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Ulat sa bug"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Kunin ang ulat sa bug"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Mangongolekta ito ng impormasyon tungkol sa kasalukuyang katayuan ng iyong device, na ipapadala bilang mensaheng e-mail. Gugugol ito ng kaunting oras mula sa pagsisimula ng ulat sa bug hanggang sa handa na itong maipadala; mangyaring magpasensya."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB drive"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB storage"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"I-edit"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Alerto sa paggamit ng data"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"I-tap tingnan paggamit/setting."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Naabot na ang limitasyon sa 2G-3G data"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Naabot na ang limitasyon sa 4G data"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index afd9e68..9983142 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefon seçenekleri"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Ekran kilidi"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Kapat"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Acil durum"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Hata raporu"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Hata raporu al"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Bu rapor, e-posta iletisi olarak göndermek üzere cihazınızın şu anki durumuyla ilgili bilgi toplar. Hata raporu başlatıldıktan sonra hazır olması biraz zaman alabilir, lütfen sabırlı olun."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB sürücüsü"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB bellek"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Düzenle"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Veri kullanımı uyarısı"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Kul. ve ayar. gör. için dokunun."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G veri sınırına ulaşıldı"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G veri sınırına ulaşıldı"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 0ce3396..4c135ab 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -218,8 +218,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Параметри телеф."</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Заблок. екран"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Вимкнути"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Екстрений виклик"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Звіт про помилки"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Звіт про помилку"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Інформація про поточний стан вашого пристрою буде зібрана й надіслана електронною поштою. Підготовка звіту триватиме певний час."</string>
@@ -1382,8 +1381,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Носій USB (<xliff:g id="MANUFACTURER">%s</xliff:g>)"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Носій USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Редагувати"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Сповіщення про використ. трафіку"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Переглянути дані та параметри."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Досягнуто ліміту даних 2G–3G"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Досягнуто ліміту даних 4G"</string>
diff --git a/core/res/res/values-ur-rPK/strings.xml b/core/res/res/values-ur-rPK/strings.xml
index ebab740..d6eae64a 100644
--- a/core/res/res/values-ur-rPK/strings.xml
+++ b/core/res/res/values-ur-rPK/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"فون کے اختیارات"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"اسکرین لاک"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"پاور آف"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"ایمرجنسی"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"بگ کی اطلاع"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"بگ کی اطلاع لیں"</string>
     <string name="bugreport_message" msgid="398447048750350456">"ایک ای میل پیغام کے بطور بھیجنے کیلئے، یہ آپ کے موجودہ آلہ کی حالت کے بارے میں معلومات جمع کرے گا۔ بگ کی اطلاع شروع کرنے سے لے کر بھیجنے کیلئے تیار ہونے تک اس میں تھوڑا وقت لگے گا؛ براہ کرم تحمل سے کام لیں۔"</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"‏<xliff:g id="MANUFACTURER">%s</xliff:g> USB ڈرائیو"</string>
     <string name="storage_usb" msgid="3017954059538517278">"‏USB اسٹوریج"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"ترمیم کریں"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"ڈیٹا کے استعمال کا الرٹ"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"استعمال اور ترتیبات دیکھنے کیلئے تھپتھپائیں۔"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"‏2G-3G ڈیٹا کی حد کو پہنچ گیا"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"‏4G ڈیٹا کی حد کو پہنچ گیا"</string>
diff --git a/core/res/res/values-uz-rUZ/strings.xml b/core/res/res/values-uz-rUZ/strings.xml
index f97a7b8..5b7cf74 100644
--- a/core/res/res/values-uz-rUZ/strings.xml
+++ b/core/res/res/values-uz-rUZ/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Telefon sozlamalari"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Ekran qulfi"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"O‘chirish"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Favqulodda chaqiruv"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Nosozlik haqida ma’lumot berish"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Xatoliklar hisoboti"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Qurilmangiz holati haqidagi ma’lumotlar to‘planib, e-pochta orqali yuboriladi. Hisobotni tayyorlash biroz vaqt olishi mumkin."</string>
@@ -1100,7 +1099,7 @@
     <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct yoniq"</string>
     <string name="wifi_p2p_enabled_notification_message" msgid="8064677407830620023">"Sozlamalarni ochish uchun bosing"</string>
     <string name="accept" msgid="1645267259272829559">"Qabul qilish"</string>
-    <string name="decline" msgid="2112225451706137894">"Rad qilish"</string>
+    <string name="decline" msgid="2112225451706137894">"Rad etish"</string>
     <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"taklif jo‘natildi"</string>
     <string name="wifi_p2p_invitation_to_connect_title" msgid="4958803948658533637">"ulanish taklifi"</string>
     <string name="wifi_p2p_from_message" msgid="570389174731951769">"Kimdan:"</string>
@@ -1114,7 +1113,7 @@
     <string name="sms_control_title" msgid="7296612781128917719">"SMS xabarlar yuborilmoqda"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; katta miqdordagi SMS xabarlarini jo‘natmoqda. Ushbu ilovaga xabarlar jo‘natishni davom ettirishga ruxsat berasizmi?"</string>
     <string name="sms_control_yes" msgid="3663725993855816807">"Ruxsat berish"</string>
-    <string name="sms_control_no" msgid="625438561395534982">"Rad qilish"</string>
+    <string name="sms_control_no" msgid="625438561395534982">"Rad etish"</string>
     <string name="sms_short_code_confirm_message" msgid="1645436466285310855">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt;ga xabar jo‘natishni xohlaydi."</string>
     <string name="sms_short_code_details" msgid="5873295990846059400">"Bunda, mobil hisobingizdan "<b>"to‘lov olinishi mumkin"</b>"."</string>
     <string name="sms_premium_short_code_details" msgid="7869234868023975"><b>"Bunda, mobil hisobingizdan to‘lov olinishi mumkin."</b></string>
@@ -1224,7 +1223,7 @@
     <string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"Ushbu so‘rovga ruxsat berishni xohlaysizmi?"</string>
     <string name="grant_permissions_header_text" msgid="6874497408201826708">"Ruxsat so‘rovi"</string>
     <string name="allow" msgid="7225948811296386551">"Ruxsat berish"</string>
-    <string name="deny" msgid="2081879885755434506">"Rad qilish"</string>
+    <string name="deny" msgid="2081879885755434506">"Rad etish"</string>
     <string name="permission_request_notification_title" msgid="6486759795926237907">"Ruxsat so‘raldi"</string>
     <string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"<xliff:g id="ACCOUNT">%s</xliff:g> hisobi uchun\nruxsat so‘raldi"</string>
     <string name="forward_intent_to_owner" msgid="1207197447013960896">"Siz ushbu ilovadan ishchi profilingizdan tashqarida foydalanmoqdasiz"</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB xotira qurilmasi"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB xotira"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Tahrirlash"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Trafik sarfi bo‘yicha ogohlantirish"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Trafik sarfi va sozlamalarni ko‘rish uchun bosing."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G trafik chekloviga yetdi"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G trafik chekloviga yetdi"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 66b87e7..42e0b0a 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Tùy chọn điện thoại"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Khoá màn hình"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Tắt nguồn"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Khẩn cấp"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Báo cáo lỗi"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Nhận báo cáo lỗi"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Báo cáo này sẽ thu thập thông tin về tình trạng thiết bị hiện tại của bạn, để gửi dưới dạng thông báo qua email. Sẽ mất một chút thời gian kể từ khi bắt đầu báo cáo lỗi cho tới khi báo cáo sẵn sàng để gửi; xin vui lòng kiên nhẫn."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"Ổ USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Bộ lưu trữ USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Chỉnh sửa"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Thông báo về sử dụng dữ liệu"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Nhấn để xem sử dụng và cài đặt."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"Đã đạt tới giới hạn dữ liệu 2G-3G"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"Đã đạt tới giới hạn dữ liệu 4G"</string>
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 2313b26..0471444 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -32,12 +32,12 @@
  -->
 <resources>
     <!-- Theme used for the intent picker activity. -->
-    <style name="Theme.DeviceDefault.Resolver" parent="Theme.Material">
+    <style name="Theme.DeviceDefault.Resolver" parent="Theme.DeviceDefault">
         <item name="colorControlActivated">?attr/colorControlHighlight</item>
         <item name="listPreferredItemPaddingStart">?attr/dialogPreferredPadding</item>
         <item name="listPreferredItemPaddingEnd">?attr/dialogPreferredPadding</item>
     </style>
 
     <!-- Use a dark theme for watches. -->
-    <style name="Theme.DeviceDefault.System" parent="Theme.Material" />
+    <style name="Theme.DeviceDefault.System" />
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 1bd1023..b67d6e2 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"手机选项"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"屏幕锁定"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"关机"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"紧急呼救"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"错误报告"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"提交错误报告"</string>
     <string name="bugreport_message" msgid="398447048750350456">"这会收集有关当前设备状态的信息,并以电子邮件的形式进行发送。从开始生成错误报告到准备好发送需要一点时间,请耐心等待。"</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> U 盘"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB存储器"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"修改"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"流量消耗提醒"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"点按即可查看使用情况和设置。"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"已达到2G-3G流量上限"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"已达到4G流量上限"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 4329c8f..62bb44a 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"手機選項"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"螢幕鎖定"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"關閉"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"緊急"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"錯誤報告"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"取得錯誤報告"</string>
     <string name="bugreport_message" msgid="398447048750350456">"這會收集您目前裝置狀態的相關資訊,並以電郵傳送給您。從開始建立錯誤報告到準備傳送需要一段時間,請耐心等候。"</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB 驅動器"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB 儲存裝置"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"編輯"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"數據用量警告"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"輕按即可查看用量和設定。"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"已達到 2G-3G 數據流量上限"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"已達到 4G 數據流量上限"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index ec7a9a0..0500c07 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"電話選項"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"螢幕鎖定"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"關機"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"緊急電話"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"錯誤報告"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"取得錯誤報告"</string>
     <string name="bugreport_message" msgid="398447048750350456">"這會收集您目前裝置狀態的相關資訊,以便透過電子郵件傳送。從錯誤報告開始建立到準備傳送的這段過程可能需要一點時間,敬請耐心等候。"</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB 隨身碟"</string>
     <string name="storage_usb" msgid="3017954059538517278">"USB 儲存裝置"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"編輯"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"數據用量警告"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"輕觸即可查看用量和設定。"</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"已達到 2G-3G 數據流量上限"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"已達到 4G 數據流量上限"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 3a19648..849f292 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -214,8 +214,7 @@
     <string name="global_actions" product="default" msgid="2406416831541615258">"Okukhethwa kukho kwefoni"</string>
     <string name="global_action_lock" msgid="2844945191792119712">"Ukuvala isikrini"</string>
     <string name="global_action_power_off" msgid="4471879440839879722">"Vala amandla"</string>
-    <!-- no translation found for global_action_emergency (7112311161137421166) -->
-    <skip />
+    <string name="global_action_emergency" msgid="7112311161137421166">"Isimo esiphuthumayo"</string>
     <string name="global_action_bug_report" msgid="7934010578922304799">"Umbiko wephutha"</string>
     <string name="bugreport_title" msgid="2667494803742548533">"Thatha umbiko wesiphazamiso"</string>
     <string name="bugreport_message" msgid="398447048750350456">"Lokhu kuzoqoqa ulwazi mayelana nesimo samanje sedivayisi yakho, ukuthumela imilayezo ye-imeyili. Kuzothatha isikhathi esincane kusuka ekuqaleni umbiko wesiphazamiso uze ulungele ukuthunyelwa; sicela ubekezele."</string>
@@ -1330,8 +1329,7 @@
     <string name="storage_usb_drive_label" msgid="4501418548927759953">"<xliff:g id="MANUFACTURER">%s</xliff:g> idrayivu ye-USB"</string>
     <string name="storage_usb" msgid="3017954059538517278">"Isitoreji se-USB"</string>
     <string name="extract_edit_menu_button" msgid="8940478730496610137">"Hlela"</string>
-    <!-- no translation found for data_usage_warning_title (3620440638180218181) -->
-    <skip />
+    <string name="data_usage_warning_title" msgid="3620440638180218181">"Izexwayiso zokusetshenziswa kwedatha"</string>
     <string name="data_usage_warning_body" msgid="6660692274311972007">"Thepha ukuze ubuke ukusetshenziswa nezilungiselelo."</string>
     <string name="data_usage_3g_limit_title" msgid="4361523876818447683">"2G-3G umkhawulo wedatha ufinyelelwe"</string>
     <string name="data_usage_4g_limit_title" msgid="4609566827219442376">"4G umkhawulo wedatha ufinyelelwe"</string>
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index 27ee27b..171f5cb 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -32,4 +32,11 @@
     <color name="accent_device_default_light">@color/accent_material_light</color>
     <color name="accent_device_default_dark">@color/accent_material_dark</color>
     <color name="accent_device_default_50">@color/material_deep_teal_50</color>
+
+    <color name="background_device_default_dark">@color/background_material_dark</color>
+    <color name="background_device_default_light">@color/background_material_light</color>
+    <color name="background_floating_device_default_dark">@color/background_floating_material_dark</color>
+    <color name="background_floating_device_default_light">@color/background_floating_material_light</color>
+    <color name="button_normal_device_default_dark">@color/btn_default_material_dark</color>
+    <color name="button_normal_device_default_light">@color/btn_default_material_light</color>
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3ae6186..3d1acdf5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2234,6 +2234,11 @@
     <!-- Flag specifying whether WFC over IMS is available on device -->
         <bool name="config_device_wfc_ims_available">false</bool>
 
+    <!-- Flag specifying whether WFC over IMS should be available for carrier: independent of
+         carrier provisioning. If false: hard disabled. If true: then depends on carrier
+         provisioning, availability etc -->
+    <bool name="config_carrier_wfc_ims_available">false</bool>
+
     <bool name="config_networkSamplingWakesDevice">true</bool>
 
     <string-array translatable="false" name="config_cdma_home_system" />
@@ -2269,21 +2274,6 @@
     <!-- Sprint need a 70 ms delay for 3way call -->
     <integer name="config_cdma_3waycall_flash_delay">0</integer>
 
-    <!-- If there is no preload VM number in the sim card, carriers such as
-         Verizon require to load a default vm number from the configurantion.
-         Define config_default_vm_number for this purpose. And there are two
-         optional formats for this configuration as below:
-         (1)<item>voicemail number</item>
-         (2)<item>voicemail number;gid</item>
-         The logic to pick up the correct voicemail number:
-         (1) If the config_default_vm_number array has no gid special item, the last one will be
-         picked
-         (2) If the config_default_vm_number array has gid special item and  it matches the current
-         sim's gid, it will be picked.
-         (3) If the config_default_vm_number array has gid special item but it doesn't match the
-         current sim's gid, the last one without gid will be picked -->
-    <string-array translatable="false" name="config_default_vm_number" />
-
     <!--SIM does not save, but the voice mail number to be changed. -->
     <bool name="editable_voicemailnumber">false</bool>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index bf7317c..cce02f2 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3561,7 +3561,7 @@
     <!-- Button text for the edit menu in input method extract mode. [CHAR LIMIT=16] -->
     <string name="extract_edit_menu_button">Edit</string>
 
-    <!-- Notification title when data usage has exceeded warning threshold. [CHAR LIMIT=32] -->
+    <!-- Notification title when data usage has exceeded warning threshold. [CHAR LIMIT=50] -->
     <string name="data_usage_warning_title">Data usage alert</string>
     <!-- Notification body when data usage has exceeded warning threshold. [CHAR LIMIT=32] -->
     <string name="data_usage_warning_body">Tap to view usage and settings.</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 449acc6..019a5e8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -347,8 +347,6 @@
   <java-symbol type="integer"  name="config_wifi_operating_voltage_mv" />
   <java-symbol type="string"  name="config_wifi_framework_sap_2G_channel_list" />
 
-  <java-symbol type="bool" name="editable_voicemailnumber" />
-
   <java-symbol type="bool" name="config_wifi_framework_cellular_handover_enable_user_triggered_adjustment" />
   <java-symbol type="integer" name="config_wifi_framework_associated_full_scan_tx_packet_threshold" />
   <java-symbol type="integer" name="config_wifi_framework_associated_full_scan_rx_packet_threshold" />
@@ -2227,6 +2225,7 @@
   <java-symbol type="bool" name="config_device_respects_hold_carrier_config" />
   <java-symbol type="bool" name="config_carrier_vt_available" />
   <java-symbol type="bool" name="config_device_wfc_ims_available" />
+  <java-symbol type="bool" name="config_carrier_wfc_ims_available" />
   <java-symbol type="attr" name="touchscreenBlocksFocus" />
   <java-symbol type="layout" name="resolver_list_with_default" />
   <java-symbol type="string" name="whichApplicationNamed" />
@@ -2267,7 +2266,6 @@
   <java-symbol type="attr" name="closeItemLayout" />
   <java-symbol type="layout" name="resolver_different_item_header" />
   <java-symbol type="integer" name="config_cdma_3waycall_flash_delay"/>
-  <java-symbol type="array" name="config_default_vm_number" />
   <java-symbol type="attr" name="windowBackgroundFallback" />
   <java-symbol type="id" name="textSpacerNoButtons" />
   <java-symbol type="array" name="dial_string_replace" />
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 0e98ade..abb0c8f 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -203,45 +203,42 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
-
+        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+        <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
+        <item name="colorButtonNormal">@color/button_normal_device_default_dark</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar -->
-    <style name="Theme.DeviceDefault.NoActionBar" parent="Theme.Material.NoActionBar">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
-        <item name="colorAccent">@color/accent_device_default_dark</item>
+    <style name="Theme.DeviceDefault.NoActionBar">
+        <item name="windowActionBar">false</item>
+        <item name="windowNoTitle">true</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar.  This theme
          sets {@link android.R.attr#windowFullscreen} to true.  -->
-    <style name="Theme.DeviceDefault.NoActionBar.Fullscreen" parent="Theme.Material.NoActionBar.Fullscreen">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
-        <item name="colorAccent">@color/accent_device_default_dark</item>
+    <style name="Theme.DeviceDefault.NoActionBar.Fullscreen">
+        <item name="windowFullscreen">true</item>
+        <item name="windowContentOverlay">@null</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
     extending in to overscan region.  This theme
     sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan}
     to true. -->
-    <style name="Theme.DeviceDefault.NoActionBar.Overscan" parent="Theme.Material.NoActionBar.Overscan">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
-        <item name="colorAccent">@color/accent_device_default_dark</item>
+    <style name="Theme.DeviceDefault.NoActionBar.Overscan">
+        <item name="windowFullscreen">true</item>
+        <item name="windowOverscan">true</item>
+        <item name="windowContentOverlay">@null</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
          system decor.  This theme sets {@link android.R.attr#windowTranslucentStatus} and
          {@link android.R.attr#windowTranslucentNavigation} to true. -->
-    <style name="Theme.DeviceDefault.NoActionBar.TranslucentDecor" parent="Theme.Material.NoActionBar.TranslucentDecor">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
-        <item name="colorAccent">@color/accent_device_default_dark</item>
+    <style name="Theme.DeviceDefault.NoActionBar.TranslucentDecor">
+        <item name="windowTranslucentStatus">true</item>
+        <item name="windowTranslucentNavigation">true</item>
+        <item name="windowContentOverlay">@null</item>
     </style>
 
     <!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
@@ -261,32 +258,29 @@
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorBackground">@color/background_floating_device_default_dark</item>
+        <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+        <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
     regular dialog. -->
-    <style name="Theme.DeviceDefault.Dialog.MinWidth" parent="Theme.Material.Dialog.MinWidth">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
-        <item name="colorAccent">@color/accent_device_default_dark</item>
+    <style name="Theme.DeviceDefault.Dialog.MinWidth">
+        <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+        <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
-    <style name="Theme.DeviceDefault.Dialog.NoActionBar" parent="Theme.Material.Dialog.NoActionBar">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
-        <item name="colorAccent">@color/accent_device_default_dark</item>
+    <style name="Theme.DeviceDefault.Dialog.NoActionBar">
+        <item name="windowActionBar">false</item>
+        <item name="windowNoTitle">true</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
     for a regular dialog. -->
-    <style name="Theme.DeviceDefault.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Dialog.NoActionBar.MinWidth">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
-        <item name="colorAccent">@color/accent_device_default_dark</item>
+    <style name="Theme.DeviceDefault.Dialog.NoActionBar.MinWidth">
+        <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+        <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -307,66 +301,52 @@
 
     <!-- DeviceDefault theme for a window that will be displayed either full-screen on smaller
     screens (small, normal) or as a dialog on larger screens (large, xlarge). -->
-    <style name="Theme.DeviceDefault.DialogWhenLarge" parent="Theme.Material.DialogWhenLarge">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
-        <item name="colorAccent">@color/accent_device_default_dark</item>
-    </style>
+    <style name="Theme.DeviceDefault.DialogWhenLarge" parent="@style/Theme.DeviceDefault" />
 
     <!-- DeviceDefault theme for a window without an action bar that will be displayed either
     full-screen on smaller screens (small, normal) or as a dialog on larger screens (large,
     xlarge). -->
-    <style name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar" parent="Theme.Material.DialogWhenLarge.NoActionBar">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
-        <item name="colorAccent">@color/accent_device_default_dark</item>
-    </style>
+    <style name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar" parent="@style/Theme.DeviceDefault.NoActionBar" />
 
     <!-- DeviceDefault theme for a presentation window on a secondary display. -->
-    <style name="Theme.DeviceDefault.Dialog.Presentation" parent="Theme.Material.Dialog.Presentation">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
-        <item name="colorAccent">@color/accent_device_default_dark</item>
-    </style>
+    <style name="Theme.DeviceDefault.Dialog.Presentation" parent="@style/Theme.DeviceDefault.NoActionBar.Fullscreen" />
 
     <!-- DeviceDefault theme for panel windows. This removes all extraneous window
     decorations, so you basically have an empty rectangle in which to place your content. It makes
     the window floating, with a transparent background, and turns off dimming behind the window. -->
-    <style name="Theme.DeviceDefault.Panel" parent="Theme.Material.Panel">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
-        <item name="colorAccent">@color/accent_device_default_dark</item>
+    <style name="Theme.DeviceDefault.Panel">
+        <item name="windowBackground">@color/transparent</item>
+        <item name="colorBackgroundCacheHint">@null</item>
+        <item name="windowFrame">@null</item>
+        <item name="windowContentOverlay">@null</item>
+        <item name="windowAnimationStyle">@null</item>
+        <item name="windowIsFloating">true</item>
+        <item name="backgroundDimEnabled">false</item>
+        <item name="windowIsTranslucent">true</item>
+        <item name="windowNoTitle">true</item>
     </style>
 
     <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
     behind them. -->
-    <style name="Theme.DeviceDefault.Wallpaper" parent="Theme.Material.Wallpaper">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
-        <item name="colorAccent">@color/accent_device_default_dark</item>
+    <style name="Theme.DeviceDefault.Wallpaper">
+        <item name="windowBackground">@color/transparent</item>
+        <item name="colorBackgroundCacheHint">@null</item>
+        <item name="windowShowWallpaper">true</item>
     </style>
 
     <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
     behind them and without an action bar. -->
-    <style name="Theme.DeviceDefault.Wallpaper.NoTitleBar" parent="Theme.Material.Wallpaper.NoTitleBar">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_dark</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
-        <item name="colorAccent">@color/accent_device_default_dark</item>
+    <style name="Theme.DeviceDefault.Wallpaper.NoTitleBar">
+        <item name="windowNoTitle">true</item>
     </style>
 
     <!-- DeviceDefault style for input methods, which is used by the
          {@link android.inputmethodservice.InputMethodService} class.-->
-    <style name="Theme.DeviceDefault.InputMethod" parent="Theme.Material.InputMethod">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
-        <item name="colorAccent">@color/accent_device_default_light</item>
+    <style name="Theme.DeviceDefault.InputMethod">
+        <item name="windowAnimationStyle">@style/Animation.InputMethod</item>
+        <item name="imeFullscreenBackground">@drawable/screen_background_selector_light</item>
+        <item name="imeExtractEnterAnimation">@anim/input_method_extract_enter</item>
+        <item name="imeExtractExitAnimation">@anim/input_method_extract_exit</item>
     </style>
 
     <!-- DeviceDefault style for input methods, which is used by the
@@ -551,53 +531,58 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
+        <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
+        <item name="colorButtonNormal">@color/button_normal_device_default_light</item>
     </style>
 
     <!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an
     inverse color profile. -->
-    <style name="Theme.DeviceDefault.Light.DarkActionBar" parent="Theme.Material.Light.DarkActionBar">
+    <style name="Theme.DeviceDefault.Light.DarkActionBar">
+        <item name="actionBarWidgetTheme">@null</item>
+        <item name="actionBarTheme">@style/ThemeOverlay.Material.Dark.ActionBar</item>
+        <item name="popupTheme">@style/ThemeOverlay.Material.Light</item>
+
         <!-- Color palette -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+        <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
-    <style name="Theme.DeviceDefault.Light.NoActionBar" parent="Theme.Material.Light.NoActionBar">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
-        <item name="colorAccent">@color/accent_device_default_light</item>
+    <style name="Theme.DeviceDefault.Light.NoActionBar">
+        <item name="windowActionBar">false</item>
+        <item name="windowNoTitle">true</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar.
          This theme sets {@link android.R.attr#windowFullscreen} to true.  -->
-    <style name="Theme.DeviceDefault.Light.NoActionBar.Fullscreen" parent="Theme.Material.Light.NoActionBar.Fullscreen">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
-        <item name="colorAccent">@color/accent_device_default_light</item>
+    <style name="Theme.DeviceDefault.Light.NoActionBar.Fullscreen">
+        <item name="windowFullscreen">true</item>
+        <item name="windowContentOverlay">@null</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
     and extending in to overscan region.  This theme
     sets {@link android.R.attr#windowFullscreen} and {@link android.R.attr#windowOverscan}
     to true. -->
-    <style name="Theme.DeviceDefault.Light.NoActionBar.Overscan" parent="Theme.Material.Light.NoActionBar.Overscan">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
-        <item name="colorAccent">@color/accent_device_default_light</item>
+    <style name="Theme.DeviceDefault.Light.NoActionBar.Overscan">
+        <item name="windowFullscreen">true</item>
+        <item name="windowOverscan">true</item>
+        <item name="windowContentOverlay">@null</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent
          system decor.  This theme sets {@link android.R.attr#windowTranslucentStatus} and
          {@link android.R.attr#windowTranslucentNavigation} to true. -->
-    <style name="Theme.DeviceDefault.Light.NoActionBar.TranslucentDecor" parent="Theme.Material.Light.NoActionBar.TranslucentDecor">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
-        <item name="colorAccent">@color/accent_device_default_light</item>
+    <style name="Theme.DeviceDefault.Light.NoActionBar.TranslucentDecor">
+        <item name="windowTranslucentStatus">true</item>
+        <item name="windowTranslucentNavigation">true</item>
+        <item name="windowContentOverlay">@null</item>
     </style>
 
     <!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
@@ -617,32 +602,29 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+        <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} that has a nice minimum width for a
     regular dialog. -->
-    <style name="Theme.DeviceDefault.Light.Dialog.MinWidth" parent="Theme.Material.Light.Dialog.MinWidth">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
-        <item name="colorAccent">@color/accent_device_default_light</item>
+    <style name="Theme.DeviceDefault.Light.Dialog.MinWidth">
+        <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+        <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
     </style>
 
      <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
-    <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar" parent="Theme.Material.Light.Dialog.NoActionBar">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
-        <item name="colorAccent">@color/accent_device_default_light</item>
+    <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar">
+        <item name="windowActionBar">false</item>
+        <item name="windowNoTitle">true</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum
     width for a regular dialog. -->
-    <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar.MinWidth" parent="Theme.Material.Light.Dialog.NoActionBar.MinWidth">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
-        <item name="colorAccent">@color/accent_device_default_light</item>
+    <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar.MinWidth">
+        <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
+        <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -651,11 +633,6 @@
         <item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
         <item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
         <item name="windowFixedHeightMinor">@dimen/dialog_fixed_height_minor</item>
-
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
-        <item name="colorAccent">@color/accent_device_default_light</item>
     </style>
 
     <!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
@@ -664,39 +641,19 @@
         <item name="windowFixedWidthMinor">@dimen/dialog_fixed_width_minor</item>
         <item name="windowFixedHeightMajor">@dimen/dialog_fixed_height_major</item>
         <item name="windowFixedHeightMinor">@dimen/dialog_fixed_height_minor</item>
-
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
-        <item name="colorAccent">@color/accent_device_default_light</item>
     </style>
 
     <!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
     screens (small, normal) or as a dialog on larger screens (large, xlarge). -->
-    <style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="Theme.Material.Light.DialogWhenLarge">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
-        <item name="colorAccent">@color/accent_device_default_light</item>
-    </style>
+    <style name="Theme.DeviceDefault.Light.DialogWhenLarge" parent="@style/Theme.DeviceDefault.Light" />
 
     <!-- DeviceDefault light theme for a window without an action bar that will be displayed either
     full-screen on smaller screens (small, normal) or as a dialog on larger screens (large,
     xlarge). -->
-    <style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" parent="Theme.Material.Light.DialogWhenLarge.NoActionBar">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
-        <item name="colorAccent">@color/accent_device_default_light</item>
-    </style>
+    <style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" parent="@style/Theme.DeviceDefault.Light.NoActionBar" />
 
     <!-- DeviceDefault light theme for a presentation window on a secondary display. -->
-    <style name="Theme.DeviceDefault.Light.Dialog.Presentation" parent="Theme.Material.Light.Dialog.Presentation">
-        <!-- Color palette -->
-        <item name="colorPrimary">@color/primary_device_default_light</item>
-        <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
-        <item name="colorAccent">@color/accent_device_default_light</item>
-    </style>
+    <style name="Theme.DeviceDefault.Light.Dialog.Presentation" parent="@style/Theme.DeviceDefault.Light.NoActionBar.Fullscreen" />
 
     <!-- DeviceDefault light theme for panel windows. This removes all extraneous window
     decorations, so you basically have an empty rectangle in which to place your content. It makes
@@ -706,6 +663,9 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+        <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
     </style>
 
     <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert">
@@ -715,6 +675,9 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+        <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
     </style>
 
     <style name="Theme.DeviceDefault.Light.SearchBar" parent="Theme.Material.Light.SearchBar">
@@ -722,6 +685,9 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+        <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
     </style>
 
     <style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice">
@@ -729,6 +695,9 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+        <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
     </style>
 
     <!-- DeviceDefault theme for a window that should look like the Settings app.  -->
@@ -738,6 +707,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+        <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
     </style>
 
     <!-- DeviceDefault theme for a window that should use Settings theme colors but has
@@ -749,6 +721,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+        <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Settings_Dark} with no action bar -->
@@ -758,6 +733,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+        <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.Dialog" parent="Theme.Material.Settings.Dialog">
@@ -766,6 +744,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+        <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.DialogWhenLarge" parent="Theme.Material.Settings.DialogWhenLarge">
@@ -774,6 +755,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+        <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
@@ -782,6 +766,9 @@
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorBackground">@color/background_device_default_dark</item>
+        <item name="colorBackgroundFloating">@color/background_floating_device_default_dark</item>
+        <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
     </style>
 
     <!-- Theme used for the intent picker activity. -->
@@ -800,6 +787,9 @@
         <item name="colorPrimary">@color/primary_device_default_light</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_light</item>
         <item name="colorAccent">@color/accent_device_default_light</item>
+        <item name="colorBackground">@color/background_device_default_light</item>
+        <item name="colorBackgroundFloating">@color/background_floating_device_default_light</item>
+        <item name="colorBackgroundCacheHint">@color/background_cache_hint_selector_device_default</item>
     </style>
 
     <!-- DeviceDefault theme for the default system theme.  -->
diff --git a/core/tests/benchmarks/src/android/os/ParcelBenchmark.java b/core/tests/benchmarks/src/android/os/ParcelBenchmark.java
index 4bd2d00..6c45ae8 100644
--- a/core/tests/benchmarks/src/android/os/ParcelBenchmark.java
+++ b/core/tests/benchmarks/src/android/os/ParcelBenchmark.java
@@ -20,12 +20,15 @@
 import com.google.caliper.BeforeExperiment;
 
 public class ParcelBenchmark {
+    private static final int INNER_REPS = 1000;
 
     private Parcel mParcel;
 
     @BeforeExperiment
     protected void setUp() {
         mParcel = Parcel.obtain();
+        mParcel.setDataPosition(0);
+        mParcel.setDataCapacity(INNER_REPS * 8);
     }
 
     @AfterExperiment
@@ -36,43 +39,58 @@
 
     public void timeWriteByte(int reps) {
         final byte val = 0xF;
-        for (int i = 0; i < reps; i++) {
-            mParcel.writeByte(val);
+        for (int i = 0; i < (reps / INNER_REPS); i++) {
+            mParcel.setDataPosition(0);
+            for (int j = 0; j < INNER_REPS; j++) {
+                mParcel.writeByte(val);
+            }
         }
     }
 
     public void timeReadByte(int reps) {
-        mParcel.setDataCapacity(reps);
-        for (int i = 0; i < reps; i++) {
-            mParcel.readByte();
+        for (int i = 0; i < (reps / INNER_REPS); i++) {
+            mParcel.setDataPosition(0);
+            for (int j = 0; j < INNER_REPS; j++) {
+                mParcel.readByte();
+            }
         }
     }
 
     public void timeWriteInt(int reps) {
         final int val = 0xF;
-        for (int i = 0; i < reps; i++) {
-            mParcel.writeInt(val);
+        for (int i = 0; i < (reps / INNER_REPS); i++) {
+            mParcel.setDataPosition(0);
+            for (int j = 0; j < INNER_REPS; j++) {
+                mParcel.writeInt(val);
+            }
         }
     }
 
     public void timeReadInt(int reps) {
-        mParcel.setDataCapacity(reps << 2);
-        for (int i = 0; i < reps; i++) {
-            mParcel.readInt();
+        for (int i = 0; i < (reps / INNER_REPS); i++) {
+            mParcel.setDataPosition(0);
+            for (int j = 0; j < INNER_REPS; j++) {
+                mParcel.readInt();
+            }
         }
     }
 
     public void timeWriteLong(int reps) {
         final long val = 0xF;
-        for (int i = 0; i < reps; i++) {
-            mParcel.writeLong(val);
+        for (int i = 0; i < (reps / INNER_REPS); i++) {
+            mParcel.setDataPosition(0);
+            for (int j = 0; j < INNER_REPS; j++) {
+                mParcel.writeLong(val);
+            }
         }
     }
 
     public void timeReadLong(int reps) {
-        mParcel.setDataCapacity(reps << 3);
-        for (int i = 0; i < reps; i++) {
-            mParcel.readLong();
+        for (int i = 0; i < (reps / INNER_REPS); i++) {
+            mParcel.setDataPosition(0);
+            for (int j = 0; j < INNER_REPS; j++) {
+                mParcel.readLong();
+            }
         }
     }
 }
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index c234b6a..ee78613 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1329,6 +1329,11 @@
             android:exported="true">
         </activity>
 
+        <activity
+                android:name="android.print.mockservice.AddPrintersActivity"
+                android:exported="true">
+        </activity>
+
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/core/tests/coretests/res/xml/printservice.xml b/core/tests/coretests/res/xml/printservice.xml
index abbebda..b105a0f0 100644
--- a/core/tests/coretests/res/xml/printservice.xml
+++ b/core/tests/coretests/res/xml/printservice.xml
@@ -17,4 +17,5 @@
 -->
 
 <print-service  xmlns:android="http://schemas.android.com/apk/res/android"
-     android:settingsActivity="android.print.mockservice.SettingsActivity"/>
+     android:settingsActivity="android.print.mockservice.SettingsActivity"
+     android:addPrintersActivity="android.print.mockservice.AddPrintersActivity" />
diff --git a/core/tests/coretests/src/android/print/BasePrintTest.java b/core/tests/coretests/src/android/print/BasePrintTest.java
index ca7b5e1..8ef8062 100644
--- a/core/tests/coretests/src/android/print/BasePrintTest.java
+++ b/core/tests/coretests/src/android/print/BasePrintTest.java
@@ -57,8 +57,7 @@
  * This is the base class for print tests.
  */
 abstract class BasePrintTest {
-
-    private static final long OPERATION_TIMEOUT = 30000;
+    protected static final long OPERATION_TIMEOUT = 30000;
     private static final String PM_CLEAR_SUCCESS_OUTPUT = "Success";
     private static final int CURRENT_USER_ID = -2; // Mirrors UserHandle.USER_CURRENT
 
@@ -75,6 +74,39 @@
             new ActivityTestRule<>(PrintTestActivity.class, false, true);
 
     /**
+     * {@link Runnable} that can throw and {@link Exception}
+     */
+    interface Invokable {
+        /**
+         * Execute the invokable
+         *
+         * @throws Exception
+         */
+        void run() throws Exception;
+    }
+
+    /**
+     * Assert that the invokable throws an expectedException
+     *
+     * @param invokable The {@link Invokable} to run
+     * @param expectedClass The {@link Exception} that is supposed to be thrown
+     */
+    void assertException(Invokable invokable, Class<? extends Exception> expectedClass)
+            throws Exception {
+        try {
+            invokable.run();
+        } catch (Exception e) {
+            if (e.getClass().isAssignableFrom(expectedClass)) {
+                return;
+            } else {
+                throw e;
+            }
+        }
+
+        throw new AssertionError("No exception thrown");
+    }
+
+    /**
      * Return the UI device
      *
      * @return the UI device
@@ -105,14 +137,14 @@
     }
 
     @Before
-    public void setUp() throws Exception {
+    public void initCounters() throws Exception {
         // Initialize the latches.
         mStartCallCounter = new CallCounter();
         mStartSessionCallCounter = new CallCounter();
     }
 
     @After
-    public void tearDown() throws Exception {
+    public void exitActivities() throws Exception {
         // Exit print spooler
         getUiDevice().pressBack();
         getUiDevice().pressBack();
diff --git a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
index 75be426..2e9c8e7 100644
--- a/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
+++ b/core/tests/coretests/src/android/print/IPrintManagerParametersTest.java
@@ -41,6 +41,7 @@
 import android.support.test.filters.LargeTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
+import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -219,10 +220,8 @@
         return new PrinterId(getActivity().getComponentName(), "dummy printer");
     }
 
-    @Override
-    public void setUp() throws Exception {
-        super.setUp();
-
+    @Before
+    public void setUpMockService() throws Exception {
         MockPrintService.setCallbacks(createMockCallbacks());
 
         mIPrintManager = IPrintManager.Stub
@@ -230,40 +229,6 @@
     }
 
     /**
-     * {@link Runnable} that can throw and {@link Exception}
-     */
-    private interface Invokable {
-        /**
-         * Execute the invokable
-         *
-         * @throws Exception
-         */
-        void run() throws Exception;
-    }
-
-    /**
-     * Assert that the invokable throws an expectedException
-     *
-     * @param invokable The {@link Invokable} to run
-     * @param expectedClass The {@link Exception} that is supposed to be thrown
-     */
-    public void assertException(Invokable invokable, Class<? extends Exception> expectedClass)
-            throws Exception {
-        try {
-            invokable.run();
-        } catch (Exception e) {
-            if (e.getClass().isAssignableFrom(expectedClass)) {
-                return;
-            } else {
-                throw new AssertionError("Expected: " + expectedClass.getName() + ", got: "
-                                + e.getClass().getName());
-            }
-        }
-
-        throw new AssertionError("No exception thrown");
-    }
-
-    /**
      * test IPrintManager.getPrintJobInfo
      */
     @LargeTest
diff --git a/core/tests/coretests/src/android/print/WorkflowTest.java b/core/tests/coretests/src/android/print/WorkflowTest.java
new file mode 100644
index 0000000..62d8b97
--- /dev/null
+++ b/core/tests/coretests/src/android/print/WorkflowTest.java
@@ -0,0 +1,454 @@
+/*
+ * 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 android.print;
+
+import android.graphics.pdf.PdfDocument;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.print.mockservice.AddPrintersActivity;
+import android.print.mockservice.MockPrintService;
+
+import android.print.mockservice.PrinterDiscoverySessionCallbacks;
+import android.print.mockservice.StubbablePrinterDiscoverySession;
+import android.print.pdf.PrintedPdfDocument;
+import android.support.test.filters.LargeTest;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiSelector;
+import android.support.test.uiautomator.Until;
+import android.util.Log;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Supplier;
+
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Tests for the basic printing workflows
+ */
+@RunWith(Parameterized.class)
+public class WorkflowTest extends BasePrintTest {
+    private static final String LOG_TAG = WorkflowTest.class.getSimpleName();
+
+    private static float sWindowAnimationScaleBefore;
+    private static float sTransitionAnimationScaleBefore;
+    private static float sAnimatiorDurationScaleBefore;
+
+    private PrintAttributes.MediaSize mFirst;
+    private boolean mSelectPrinter;
+    private PrintAttributes.MediaSize mSecond;
+
+    public WorkflowTest(PrintAttributes.MediaSize first, boolean selectPrinter,
+            PrintAttributes.MediaSize second) {
+        mFirst = first;
+        mSelectPrinter = selectPrinter;
+        mSecond = second;
+    }
+
+    interface InterruptableConsumer<T> {
+        void accept(T t) throws InterruptedException;
+    }
+
+    /**
+     * Execute {@code waiter} until {@code condition} is met.
+     *
+     * @param condition Conditions to wait for
+     * @param waiter    Code to execute while waiting
+     */
+    private void waitWithTimeout(Supplier<Boolean> condition, InterruptableConsumer<Long> waiter)
+            throws TimeoutException, InterruptedException {
+        long startTime = System.currentTimeMillis();
+        while (condition.get()) {
+            long timeLeft = OPERATION_TIMEOUT - (System.currentTimeMillis() - startTime);
+            if (timeLeft < 0) {
+                throw new TimeoutException();
+            }
+
+            waiter.accept(timeLeft);
+        }
+    }
+
+    /**
+     * Executes a shell command using shell user identity, and return the standard output in
+     * string.
+     *
+     * @param cmd the command to run
+     *
+     * @return the standard output of the command
+     */
+    private static String runShellCommand(String cmd) throws IOException {
+        try (FileInputStream is = new ParcelFileDescriptor.AutoCloseInputStream(
+                getInstrumentation().getUiAutomation().executeShellCommand(cmd))) {
+            byte[] buf = new byte[64];
+            int bytesRead;
+
+            StringBuilder stdout = new StringBuilder();
+            while ((bytesRead = is.read(buf)) != -1) {
+                stdout.append(new String(buf, 0, bytesRead));
+            }
+
+            return stdout.toString();
+        }
+    }
+
+    @BeforeClass
+    public static void disableAnimations() throws Exception {
+        try {
+            sWindowAnimationScaleBefore = Float.parseFloat(runShellCommand(
+                    "settings get global window_animation_scale"));
+
+            runShellCommand("settings put global window_animation_scale 0");
+        } catch (NumberFormatException e) {
+            sWindowAnimationScaleBefore = Float.NaN;
+        }
+        try {
+            sTransitionAnimationScaleBefore = Float.parseFloat(runShellCommand(
+                    "settings get global transition_animation_scale"));
+
+            runShellCommand("settings put global transition_animation_scale 0");
+        } catch (NumberFormatException e) {
+            sTransitionAnimationScaleBefore = Float.NaN;
+        }
+        try {
+            sAnimatiorDurationScaleBefore = Float.parseFloat(runShellCommand(
+                    "settings get global animator_duration_scale"));
+
+            runShellCommand("settings put global animator_duration_scale 0");
+        } catch (NumberFormatException e) {
+            sAnimatiorDurationScaleBefore = Float.NaN;
+        }
+    }
+
+    @AfterClass
+    public static void enableAnimations() throws Exception {
+        if (sWindowAnimationScaleBefore != Float.NaN) {
+            runShellCommand(
+                    "settings put global window_animation_scale " + sWindowAnimationScaleBefore);
+        }
+        if (sTransitionAnimationScaleBefore != Float.NaN) {
+            runShellCommand(
+                    "settings put global transition_animation_scale " +
+                            sTransitionAnimationScaleBefore);
+        }
+        if (sAnimatiorDurationScaleBefore != Float.NaN) {
+            runShellCommand(
+                    "settings put global animator_duration_scale " + sAnimatiorDurationScaleBefore);
+        }
+    }
+
+    /** Add a printer with a given name and supported mediasize to a session */
+    private void addPrinter(StubbablePrinterDiscoverySession session,
+            String name, PrintAttributes.MediaSize mediaSize) {
+        PrinterId printerId = session.getService().generatePrinterId(name);
+        List<PrinterInfo> printers = new ArrayList<>(1);
+
+        PrinterCapabilitiesInfo.Builder builder =
+                new PrinterCapabilitiesInfo.Builder(printerId);
+
+        PrinterInfo printerInfo;
+        if (mediaSize != null) {
+            builder.setMinMargins(new PrintAttributes.Margins(0, 0, 0, 0))
+                    .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+                            PrintAttributes.COLOR_MODE_COLOR)
+                    .addMediaSize(mediaSize, true)
+                    .addResolution(new PrintAttributes.Resolution("300x300", "300x300", 300, 300),
+                            true);
+
+            printerInfo = new PrinterInfo.Builder(printerId, name,
+                    PrinterInfo.STATUS_IDLE).setCapabilities(builder.build()).build();
+        } else {
+            printerInfo = (new PrinterInfo.Builder(printerId, name,
+                    PrinterInfo.STATUS_IDLE)).build();
+        }
+
+        printers.add(printerInfo);
+        session.addPrinters(printers);
+    }
+
+    /** Find a certain element in the UI and click on it */
+    private void clickOn(UiSelector selector) throws UiObjectNotFoundException {
+        Log.i(LOG_TAG, "Click on " + selector);
+        UiObject view = getUiDevice().findObject(selector);
+        view.click();
+        getUiDevice().waitForIdle();
+    }
+
+    /** Find a certain text in the UI and click on it */
+    private void clickOnText(String text) throws UiObjectNotFoundException {
+        clickOn(new UiSelector().text(text));
+    }
+
+    /** Set the printer in the print activity */
+    private void setPrinter(String printerName) throws UiObjectNotFoundException {
+        clickOn(new UiSelector().resourceId("com.android.printspooler:id/destination_spinner"));
+
+        clickOnText(printerName);
+    }
+
+    /**
+     * Init mock print servic that returns a single printer by default.
+     *
+     * @param sessionRef Where to store the reference to the session once started
+     */
+    private void setMockPrintServiceCallbacks(StubbablePrinterDiscoverySession[] sessionRef,
+            ArrayList<String> trackedPrinters, PrintAttributes.MediaSize mediaSize) {
+        MockPrintService.setCallbacks(createMockPrintServiceCallbacks(
+                inv -> createMockPrinterDiscoverySessionCallbacks(inv2 -> {
+                            synchronized (sessionRef) {
+                                sessionRef[0] = ((PrinterDiscoverySessionCallbacks) inv2.getMock())
+                                        .getSession();
+
+                                addPrinter(sessionRef[0], "1st printer", mediaSize);
+
+                                sessionRef.notifyAll();
+                            }
+                            return null;
+                        },
+                        null, null, inv2 -> {
+                            synchronized (trackedPrinters) {
+                                trackedPrinters
+                                        .add(((PrinterId) inv2.getArguments()[0]).getLocalId());
+                                trackedPrinters.notifyAll();
+                            }
+                            return null;
+                        }, null, inv2 -> {
+                            synchronized (trackedPrinters) {
+                                trackedPrinters
+                                        .remove(((PrinterId) inv2.getArguments()[0]).getLocalId());
+                                trackedPrinters.notifyAll();
+                            }
+                            return null;
+                        }, inv2 -> {
+                            synchronized (sessionRef) {
+                                sessionRef[0] = null;
+                                sessionRef.notifyAll();
+                            }
+                            return null;
+                        }
+                ), null, null));
+    }
+
+    /**
+     * Start print operation that just prints a single empty page
+     *
+     * @param printAttributesRef Where to store the reference to the print attributes once started
+     */
+    private void print(PrintAttributes[] printAttributesRef) {
+        print(new PrintDocumentAdapter() {
+            @Override
+            public void onStart() {
+            }
+
+            @Override
+            public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
+                    CancellationSignal cancellationSignal, LayoutResultCallback callback,
+                    Bundle extras) {
+                callback.onLayoutFinished((new PrintDocumentInfo.Builder("doc")).build(),
+                        !newAttributes.equals(printAttributesRef[0]));
+
+                synchronized (printAttributesRef) {
+                    printAttributesRef[0] = newAttributes;
+                    printAttributesRef.notifyAll();
+                }
+            }
+
+            @Override
+            public void onWrite(PageRange[] pages, ParcelFileDescriptor destination,
+                    CancellationSignal cancellationSignal, WriteResultCallback callback) {
+                try {
+                    try {
+                        PrintedPdfDocument document = new PrintedPdfDocument(getActivity(),
+                                printAttributesRef[0]);
+                        try {
+                            PdfDocument.Page page = document.startPage(0);
+                            document.finishPage(page);
+                            try (FileOutputStream os = new FileOutputStream(
+                                    destination.getFileDescriptor())) {
+                                document.writeTo(os);
+                                os.flush();
+                            }
+                        } finally {
+                            document.close();
+                        }
+                    } finally {
+                        destination.close();
+                    }
+
+                    callback.onWriteFinished(pages);
+                } catch (IOException e) {
+                    callback.onWriteFailed(e.getMessage());
+                }
+            }
+        }, null);
+    }
+
+    @Parameterized.Parameters
+    public static Collection<Object[]> getParameters() {
+        ArrayList<Object[]> tests = new ArrayList<>();
+
+        for (PrintAttributes.MediaSize first : new PrintAttributes.MediaSize[]{
+                PrintAttributes.MediaSize.ISO_A0, null}) {
+            for (Boolean selectPrinter : new Boolean[]{true, false}) {
+                for (PrintAttributes.MediaSize second : new PrintAttributes.MediaSize[]{
+                        PrintAttributes.MediaSize.ISO_A1, null}) {
+                    // If we do not use the second printer, no need to try various options
+                    if (!selectPrinter && second == null) {
+                        continue;
+                    }
+                    tests.add(new Object[]{first, selectPrinter, second});
+                }
+            }
+        }
+
+        return tests;
+    }
+
+    @Test
+    @LargeTest
+    public void addAndSelectPrinter() throws Exception {
+        final StubbablePrinterDiscoverySession session[] = new StubbablePrinterDiscoverySession[1];
+        final PrintAttributes printAttributes[] = new PrintAttributes[1];
+        ArrayList<String> trackedPrinters = new ArrayList<>();
+
+        Log.i(LOG_TAG, "Running " + mFirst + " " + mSelectPrinter + " " + mSecond);
+
+        setMockPrintServiceCallbacks(session, trackedPrinters, mFirst);
+        print(printAttributes);
+
+        // We are now in the PrintActivity
+        Log.i(LOG_TAG, "Waiting for session");
+        synchronized (session) {
+            waitWithTimeout(() -> session[0] == null, session::wait);
+        }
+
+        setPrinter("1st printer");
+
+        Log.i(LOG_TAG, "Waiting for 1st printer to be tracked");
+        synchronized (trackedPrinters) {
+            waitWithTimeout(() -> !trackedPrinters.contains("1st printer"), trackedPrinters::wait);
+        }
+
+        if (mFirst != null) {
+            Log.i(LOG_TAG, "Waiting for print attributes to change");
+            synchronized (printAttributes) {
+                waitWithTimeout(
+                        () -> printAttributes[0] == null ||
+                                !printAttributes[0].getMediaSize().equals(
+                                        mFirst), printAttributes::wait);
+            }
+        } else {
+            Log.i(LOG_TAG, "Waiting for error message");
+            assertNotNull(getUiDevice().wait(Until.findObject(
+                    By.text("This printer isn't available right now.")), OPERATION_TIMEOUT));
+        }
+
+        setPrinter("All printers\u2026");
+
+        // We are now in the SelectPrinterActivity
+        clickOnText("Add printer");
+
+        // We are now in the AddPrinterActivity
+        AddPrintersActivity.addObserver(
+                () -> addPrinter(session[0], "2nd printer", mSecond));
+
+        // This executes the observer registered above
+        clickOn(new UiSelector().text(MockPrintService.class.getCanonicalName())
+                .resourceId("com.android.printspooler:id/title"));
+
+        getUiDevice().pressBack();
+        AddPrintersActivity.clearObservers();
+
+        if (mSelectPrinter) {
+            // We are now in the SelectPrinterActivity
+            clickOnText("2nd printer");
+        } else {
+            getUiDevice().pressBack();
+        }
+
+        // We are now in the PrintActivity
+        if (mSelectPrinter) {
+            if (mSecond != null) {
+                Log.i(LOG_TAG, "Waiting for print attributes to change");
+                synchronized (printAttributes) {
+                    waitWithTimeout(
+                            () -> printAttributes[0] == null ||
+                                    !printAttributes[0].getMediaSize().equals(
+                                            mSecond), printAttributes::wait);
+                }
+            } else {
+                Log.i(LOG_TAG, "Waiting for error message");
+                assertNotNull(getUiDevice().wait(Until.findObject(
+                        By.text("This printer isn't available right now.")), OPERATION_TIMEOUT));
+            }
+
+            Log.i(LOG_TAG, "Waiting for 1st printer to be not tracked");
+            synchronized (trackedPrinters) {
+                waitWithTimeout(() -> trackedPrinters.contains("1st printer"),
+                        trackedPrinters::wait);
+            }
+
+            Log.i(LOG_TAG, "Waiting for 2nd printer to be tracked");
+            synchronized (trackedPrinters) {
+                waitWithTimeout(() -> !trackedPrinters.contains("2nd printer"),
+                        trackedPrinters::wait);
+            }
+        } else {
+            Thread.sleep(100);
+
+            if (mFirst != null) {
+                Log.i(LOG_TAG, "Waiting for print attributes to change");
+                synchronized (printAttributes) {
+                    waitWithTimeout(
+                            () -> printAttributes[0] == null ||
+                                    !printAttributes[0].getMediaSize().equals(
+                                            mFirst), printAttributes::wait);
+                }
+            } else {
+                Log.i(LOG_TAG, "Waiting for error message");
+                assertNotNull(getUiDevice().wait(Until.findObject(
+                        By.text("This printer isn't available right now.")), OPERATION_TIMEOUT));
+            }
+
+            Log.i(LOG_TAG, "Waiting for 1st printer to be tracked");
+            synchronized (trackedPrinters) {
+                waitWithTimeout(() -> !trackedPrinters.contains("1st printer"),
+                        trackedPrinters::wait);
+            }
+        }
+
+        getUiDevice().pressBack();
+
+        // We are back in the test activity
+        Log.i(LOG_TAG, "Waiting for session to end");
+        synchronized (session) {
+            waitWithTimeout(() -> session[0] != null, session::wait);
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/print/mockservice/AddPrintersActivity.java b/core/tests/coretests/src/android/print/mockservice/AddPrintersActivity.java
new file mode 100644
index 0000000..8f1a9ed
--- /dev/null
+++ b/core/tests/coretests/src/android/print/mockservice/AddPrintersActivity.java
@@ -0,0 +1,52 @@
+/*
+ * 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 android.print.mockservice;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+
+import java.util.ArrayList;
+
+public class AddPrintersActivity extends Activity {
+    private static final ArrayList<Runnable> sObservers = new ArrayList<>();
+
+    public static void addObserver(@NonNull Runnable observer) {
+        synchronized (sObservers) {
+            sObservers.add(observer);
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        synchronized (sObservers) {
+            for (Runnable sObserver : sObservers) {
+                sObserver.run();
+            }
+        }
+
+        finish();
+    }
+
+    public static void clearObservers() {
+        synchronized (sObservers) {
+            sObservers.clear();
+        }
+    }
+}
diff --git a/docs/html/_redirects.yaml b/docs/html/_redirects.yaml
index 8ddb982..3a993d7 100644
--- a/docs/html/_redirects.yaml
+++ b/docs/html/_redirects.yaml
@@ -430,7 +430,13 @@
 - from: /training/cloudsync/aesync.html
   to: /google/gcm/index.html
 - from: /training/cloudsync/index.html
-  to: /training/backup/index.html
+  to: /guide/topics/data/backup.html
+- from: /training/backup/index.html
+  to: /guide/topics/data/backup.html
+- from: /training/backup/autosyncapi.html
+  to: /guide/topics/data/autobackup.html
+- from: /training/backup/backupapi.html
+  to: /guide/topics/data/keyvaluebackup.html
 - from: /training/basics/location/...
   to: /training/location/...
 - from: /training/monetization/index.html
@@ -796,7 +802,7 @@
 - from: /preview/features/app-linking.html
   to: /training/app-links/index.html
 - from: /preview/backup/index.html
-  to: /training/backup/autosyncapi.html
+  to: /guide/topics/data/backup/autobackup.html
 - from: /preview/features/power-mgmt.html
   to: /training/monitoring-device-state/doze-standby.html
 - from: /preview/dev-community
@@ -842,6 +848,10 @@
   to: /topic/performance/power/network/gather-data.html
 - from: /training/performance/battery/network/index.html
   to: /topic/performance/power/network/index.html
+- from: /training/articles/memory.html
+  to: /topic/performance/memory.html
+- from: /topic/performance/optimizing-view-hierarchies.html
+  to: /topic/performance/rendering/optimizing-view-hierarchies.html
 
 # Redirects for the new [dac]/topic/libraries/ area
 
@@ -1242,6 +1252,46 @@
   to: /studio/write/lint.html?utm_source=android-studio
 - from: /r/studio-ui/gradle-console.html
   to: /studio/run/index.html?utm_source=android-studio#gradle-console
+- from: /r/studio-ui/app-indexing-test.html
+  to: /studio/write/app-link-indexing.html#appindexingtest?utm_source=android-studio
+- from: /r/studio-ui/vcs.html
+  to: /studio/intro/index.html#version_control_basics?utm_source=android-studio
+- from: /r/studio-ui/create-new-module.html
+  to: /studio/projects/index.html#ApplicationModules?utm_source=android-studio
+- from: /r/studio-ui/build-variants.html
+  to: /studio/run/index.html#changing-variant?utm_source=android-studio
+- from: /r/studio-ui/generate-signed-apk.html
+  to: /studio/publish/app-signing.html#release-mode?utm_source=android-studio
+- from: /r/studio-ui/import-project-vcs.html
+  to: /studio/projects/create-project.html#ImportAProject?utm_source=android-studio
+- from: /r/studio-ui/apk-analyzer.html
+  to: /studio/build/apk-analyzer.html?utm_source=android-studio
+- from: /r/studio-ui/breakpoints.html
+  to: /studio/debug/index.html#breakPointsView?utm_source=android-studio
+- from: /r/studio-ui/attach-debugger-to-process.html
+  to: /studio/debug/index.html?utm_source=android-studio
+- from: /r/studio-ui/import-sample.html
+  to: /samples/index.html?utm_source=android-studio
+- from: /r/studio-ui/import-module.html
+  to: /studio/projects/add-app-module.html#ImportAModule?utm_source=android-studio
+- from: /r/studio-ui/import-project.html
+  to: /studio/projects/create-project.html#ImportAProject?utm_source=android-studio
+- from: /r/studio-ui/create-project.html
+  to: /studio/projects/create-project.html?utm_source=android-studio
+- from: /r/studio-ui/new-activity.html
+  to: /studio/projects/template.html?utm_source=android-studio
+- from: /r/studio-ui/new-resource-file.html
+  to: /studio/write/add-resources.html?utm_source=android-studio
+- from: /r/studio-ui/new-resource-dir.html
+  to: /studio/write/add-resources.html#add_a_resource_directory?utm_source=android-studio
+- from: /r/studio-ui/configure-component.html
+  to: /studio/write/add-resources.html?utm_source=android-studio
+- from: /r/studio-ui/ninepatch.html
+  to: /studio/write/draw9patch.html?utm_source=android-studio
+- from: /r/studio-ui/firebase-assistant.html
+  to: /studio/write/firebase.html?utm_source=android-studio
+- from: /r/studio-ui/ir-flight-recorder.html
+  to: /studio/run/index.html?utm_source=android-studio#submit-feedback
 
 # Redirects from (removed) N Preview documentation
 - from: /preview/features/afw.html
diff --git a/docs/html/guide/_book.yaml b/docs/html/guide/_book.yaml
index 13c948c..f09fe77 100644
--- a/docs/html/guide/_book.yaml
+++ b/docs/html/guide/_book.yaml
@@ -396,6 +396,13 @@
     path: /guide/topics/data/data-storage.html
   - title: Data Backup
     path: /guide/topics/data/backup.html
+    section:
+    - title: Auto Backup
+      path: /guide/topics/data/autobackup.html
+    - title: Key/Value Backup
+      path: /guide/topics/data/keyvaluebackup.html
+    - title: Testing Backup and Restore
+      path: /guide/topics/data/testingbackup.html
   - title: App Install Location
     path: /guide/topics/data/install-location.html
 
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index 9257a76..8fe3f20 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -545,9 +545,16 @@
          <li><a href="<?cs var:toroot ?>guide/topics/data/data-storage.html">
             <span class="en">Storage Options</span>
            </a></li>
-        <li><a href="<?cs var:toroot ?>guide/topics/data/backup.html">
+        <li class="nav-section">
+          <div class="nav-section-header"><a href="<?cs var:toroot ?>guide/topics/data/backup.html">
             <span class="en">Data Backup</span>
-          </a></li>
+          </a></div>
+          <ul>
+            <li><a href="<?cs var:toroot ?>guide/topics/data/autobackup.html">Auto Backup</a></li>
+            <li><a href="<?cs var:toroot ?>guide/topics/data/keyvaluebackup.html">Key/Value Backup</a></li>
+            <li><a href="<?cs var:toroot ?>guide/topics/data/testingbackup.html">Testing Backup and Restore</a></li>
+          </ul>
+        </li>
         <li><a href="<?cs var:toroot ?>guide/topics/data/install-location.html">
             <span class="en">App Install Location</span>
           </a></li>
diff --git a/docs/html/guide/topics/data/autobackup.jd b/docs/html/guide/topics/data/autobackup.jd
new file mode 100644
index 0000000..3be09d7
--- /dev/null
+++ b/docs/html/guide/topics/data/autobackup.jd
@@ -0,0 +1,257 @@
+page.title=Auto Backup for Apps
+page.tags=backup, marshmallow, androidm
+page.keywords=backup, autobackup
+page.image=images/cards/card-auto-backup_2x.png
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#Files">Files that are backed up</a></li>
+    <li><a href="#BackupLocation">Backup location</a></li>
+    <li><a href="#BackupSchedule">Backup schedule</a></li>
+    <li><a href="#RestoreSchedule">Restore schedule</a></li>
+    <li><a href="#EnablingAutoBackup">Enabling and disabling Auto Backup</a></li>
+    <li><a href="#IncludingFiles">Including and excluding files</a><ul>
+      <li><a href="#XMLSyntax">XML config syntax</a></li>
+    </ul></li>
+    <li><a href="#ImplementingBackupAgent">Implementing BackupAgent</a></li>
+  </ol>
+
+  <h2>Key classes</h2>
+  <ol>
+    <li>{@link android.app.backup.BackupAgent}</li>
+    <li><a href="{@docRoot}reference/android/R.attr.html">R.attr</a></li>
+  </ol>
+
+</div>
+</div>
+
+Since Android 6.0 (API 23), Android has offered the <em>Auto Backup for Apps</em> feature as
+a way for developers to quickly add backup functionality to their apps. Auto
+Backup preserves app data by uploading it to the user’s Google Drive account.
+The amount of data is limited to 25MB per user of your app and there is no
+charge for storing backup data.
+
+<h2 id="Files">Files that are backed up</h2>
+<p>By default, Auto Backup includes files in most of the directories that are
+assigned to your app by the system:
+<ul>
+  <li>Shared preferences files.
+  <li>Files in the directory returned by {@link android.content.Context#getFilesDir()}.
+  <li>Files in the directory returned by {@link android.content.Context#getDatabasePath(String)},
+    which also includes files created with the
+    {@link android.database.sqlite.SQLiteOpenHelper} class.
+  <li>Files in directories created with {@link android.content.Context#getDir(String,int)}.
+  <li>Files on external storage in the directory returned by
+    {@link android.content.Context#getExternalFilesDir(String)}.</li></ul>
+
+<p>Auto Backup excludes files in directories returned by
+  {@link android.content.Context#getCacheDir()},
+  {@link android.content.Context#getCodeCacheDir()}, or
+  {@link android.content.Context#getNoBackupFilesDir()}. The files saved
+  in these locations are only needed temporarily, or are intentionally
+  excluded from backup operations.
+
+<p>You can configure your app to include and exclude particular files.
+For more information, see the <a href="#IncludingFiles">Include and exclude files</a>
+section.
+
+<h2 id="BackupLocation">Backup location</h2>
+<p>Backup data is stored in a private folder in the user's Google Drive account,
+limited to 25MB per app. The saved data does not count towards the user's
+personal Google Drive quota. Only the most recent backup is stored. When a
+backup is made, the previous backup (if one exists) is deleted.
+
+<p>Users can see a list of apps that have been backed up in the Google Drive app under
+<strong>Settings -> Auto Backup for apps -> Manage backup</strong>. The
+backup data cannot be read by the user or other applications on the device.
+
+<p>Backups from each device-setup-lifetime are stored in separate datasets
+as shown in the following examples:
+<ul>
+  <li>If the user owns two devices, then a backup dataset exists for each device.
+  <li>If the user factory resets a device and then sets up the device with the
+    same account, the backup is stored in a new dataset. Obsolete datasets are
+    automatically deleted after a period of inactivity.</li></ul>
+
+<p class="caution"><strong>Caution:</strong> Once the amount of data reaches
+25MB, the app is banned from sending data to the
+cloud, even if the amount of data later falls under the 25MB threshold. The ban
+affects only the offending device (not other devices that the user owns) and
+lasts for the entire device-setup-lifetime. For example, if the user removes and
+reinstalls the application, the ban is still in effect. The ban is lifted when
+the user performs factory reset on the device.
+
+<h2 id="BackupSchedule">Backup schedule</h2>
+<p>Backups occur automatically when all of the following conditions are met:
+<ul>
+  <li>The user has enabled backup on the device in <strong>Settings</strong> >
+    <strong>Backup &amp; Reset</strong>.
+  <li>At least 24 hours have elapsed since the last backup.
+  <li>The device is idle and charging.
+  <li>The device is connected to a Wi-Fi network. If the device is never connected
+    to a wifi network, then Auto Backup never occurs.</li></ul>
+
+<p>In practice, these conditions occur roughly every night. To conserve network
+bandwidth, upload takes place only if app data has changed.
+
+<p>During Auto Backup, the system shuts down the app to make sure it is no longer
+writing to the file system. By default, the backup system ignores apps that are
+running in the foreground because users would notice their apps being shut down.
+You can override the default behavior by setting the
+<a href="{@docRoot}reference/android/R.attr.html#backupInForeground">backupInForeground</a>
+attribute to true.
+
+<p>To simplify testing, Android includes tools that let you manually initiate
+a backup of your app. For more information, see
+<a href="{@docRoot}guide/topics/data/testingbackup.html">Testing Backup and Restore</a>.
+
+<h2 id="RestoreSchedule">Restore schedule</h2>
+<p>Data is restored whenever the app is installed, either from the Play store,
+during device setup (when the system installs previously installed apps), or
+from running adb install. The restore operation occurs after the APK is
+installed, but before the app is available to be launched by the user.
+
+<p>During the initial device setup wizard, the user is shown a list of available backup
+datasets and is asked which one to restore the data from. Whichever backup
+dataset is selected becomes the ancestral dataset for the device. The device can
+restore from either its own backups or the ancestral dataset. The device
+prioritize its own backup if backups from both sources are available. If the
+user didn't go through the device setup wizard, then the device can restore only from
+its own backups.
+
+<p>To simplify testing, Android includes tools that let you manually initiate
+a restore of your app. For more information, see
+<a href="{@docRoot}guide/topics/data/testingbackup.html">Testing Backup and Restore</a>.
+
+<h2 id="EnablingAutoBackup">Enabling and disabling backup</h2>
+<p>Apps that target Android 6.0 (API level 23) or higher automatically participate
+in Auto Backup. This is because the
+<a href="{@docRoot}reference/android/R.attr.html#allowBackup">android:allowBackup</a>
+attribute, which enables/disables backup, defaults to <code>true</code> if omitted.
+To avoid confusion, we recommend you explicitly set the attribute in the <code>&lt;application></code>
+element of your <code>AndroidManifest.xml</code>. For example:
+
+<pre class="prettyprint">&lt;application ...
+    android:allowBackup="true">
+&lt;/app></pre>
+
+<p>To disable Auto Backup, add either of the following attributes to the
+application element in your manifest file:
+
+<ul>
+  <li>set <code>android:allowBackup</code> to <code>false</code>. This completely disables data
+    backup. You may want to disable backups when your app can recreate its state
+    through some other mechanism or when your app deals with sensitive
+    information that should not be backed up.</li>
+  <li>set <code>android:allowBackup</code> to <code>true</code> and
+    <code>android:fullBackupOnly</code> to <code>false</code>. With these settings,
+    your app always participates in Key/Value Backup, even when running on devices that
+    support Auto Backup.</li></ul>
+
+<h2 id="IncludingFiles">Including and excluding files</h2>
+<p>By default, the system backs up almost all app data. For more information,
+see <a href="#Files">Files that are backed up</a>. This section shows you how to
+define custom XML rules to control what gets backed up.
+
+<ol>
+  <li>In <code>AndroidManifest.xml</code>, add the <a href="{@docRoot}reference/android/R.attr.html#fullBackupContent">android:fullBackupContent</a> attribute to the
+  <code>&lt;application></code> element. This attribute points to an XML file that contains backup
+  rules. For example:
+  <pre class="prettyprint">&lt;application ...
+    android:fullBackupContent="@xml/my_backup_rules">
+  &lt;/app></pre></li>
+  <li>Create an XML file called <code>my_backup_rules.xml</code> in the <code>res/xml/</code> directory. Inside the file, add rules with the <code>&lt;include></code> and <code>&lt;exclude></code> elements. The following sample backs up all shared preferences except <code>device.xml</code>:
+  <pre>&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;full-backup-content>
+    &lt;include domain="sharedpref" path="."/>
+    &lt;exclude domain="sharedpref" path="device.xml"/>
+&lt;/full-backup-content></pre></li>
+
+<h3 id="XMLSyntax">XML Config Syntax</h3>
+<p>The XML syntax for the configuration file is shown below:
+
+<pre class="prettyprint">&lt;full-backup-content>
+    &lt;include domain=["file" | "database" | "sharedpref" | "external" | "root"]
+    path="string" />
+    &lt;exclude domain=["file" | "database" | "sharedpref" | "external" | "root"]
+    path="string" />
+&lt;/full-backup-content></pre>
+
+<p>Inside the <code>&lt;full-backup-content></code> tag, you can define <code>&lt;include></code> and <code>&lt;exclude></code>
+elements:
+
+<ul>
+  <li><code>&lt;include></code> - Specifies a file or folder to backup. By default, Auto Backup
+includes almost all app files. If you specify an &lt;include> element, the system
+no longer includes any files by default and backs up <em>only the files
+specified</em>. To include multiple files, use multiple &lt;include> elements.
+  <p>note: Files in directories returned by <code>getCacheDir()</code>, <code>getCodeCacheDir()</code>, or
+<code>getNoBackupFilesDir()</code> are always excluded even if you try to include them.</li>
+
+  <li><code>&lt;exclude></code> - Specifies a file or folder to exclude during backup. Here are
+some files that are typically excluded from backup: <ul>
+    <li>Files that have device specific identifiers, either issued by a server or
+generated on the device. For example, <a href="https://developers.google.com/cloud-messaging/android/start">Google Cloud Messaging (GCM)</a> needs to
+generate a registration token every time a user installs your app on a new
+device. If the old registration token is restored, the app may behave
+unexpectedly.
+    <li>Account credentials or other sensitive information. Consider asking the
+user to reauthenticate the first time they launch a restored app rather than
+allowing for storage of such information in the backup.
+    <li>Files related to app debugging, such as <a href="{@docRoot}studio/run/index.html#instant-run">instant run files</a>. To exclude instant run files, add the rule <code>&lt;exclude
+domain="file" path="instant-run"/></code>
+    <li>Large files that cause the app to exceed the 25MB backup quota.</li> </ul>
+  </li> </ul>
+
+<p class="note"><strong>Note:</strong> If your configuration file specifies both elements, then the
+backup contains everything captured by the <code>&lt;include></code> elements minus the
+resources named in the <code>&lt;exclude></code> elements. In other words,
+<code>&lt;exclude></code> takes precedence.
+
+<p>Each element must include the following two attributes:
+<ul>
+  <li><code>domain</code> - specifies the location of resource. Valid values for this attribute
+include the following: <ul>
+    <li><code>root</code> - the directory on the filesystem where all private files belonging to
+this app are stored.
+    <li><code>file</code> - directories returned by {@link android.content.Context#getFilesDir()}.
+    <li><code>database</code> - directories returned by {@link android.content.Context#getDatabasePath(String) getDatabasePath()}.
+Databases created with {@link android.database.sqlite.SQLiteOpenHelper}
+are stored here.
+    <li><code>sharedpref</code> - the directory where {@link android.content.SharedPreferences}
+are stored.
+    <li><code>external</code> the directory returned by {@link android.content.Context#getExternalFilesDir(String) getExternalFilesDir()}
+    <p>Note: You cannot backup files outside of these locations.</li></ul>
+  <li><code>path</code>: Specifies a file or folder to include in or exclude from backup. Note
+that: <ul>
+    <li>This attribute does not support wildcard or regex syntax.
+    <li>You can use <code>.</code> to reference the current directory, however, you cannot
+reference the parent directory <code>..</code> for security reasons.
+    <li>If you specify a directory, then the rule applies to all files in the
+directory and recursive sub-directories.</li></ul></li></ul>
+
+<h2 id="ImplementingBackupAgent">Implementing BackupAgent</h2>
+<p>Apps that implement Auto Backup do not need to implement {@link android.app.backup.BackupAgent}. However, you can optionally implement a custom {@link android.app.backup.BackupAgent}. Typically, there are two reasons for doing this:
+<ul>
+  <li>You want to receive notification of backup events such as,
+{@link android.app.backup.BackupAgent#onRestoreFinished()} or {@link android.app.backup.BackupAgent#onQuotaExceeded(long,long)}. These callback methods are executed
+even if the app is not running.
+<li>You can't easily express the set of files you want to backup with XML rules.
+In these very rare cases, you can implement a BackupAgent that overrides {@link android.app.backup.BackupAgent#onFullBackup(FullBackupDataOutput)} to
+store what you want. To retain the system's default implementation, call the
+corresponding method on the superclass with <code>super.onFullBackup()</code>.</li></ul>
+
+<p class="note"><strong>Note:</strong> Your <code>BackupAgent</code> must
+implement the abstract methods
+{@link android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor) onBackup()}
+and {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}.
+Those methods are used for Key/Value Backup. So if
+you are not using Key/Value Backup, implement those methods and leave them blank.
+
+<p>For more information, see
+<a href="{@docRoot}guide/topics/data/keyvaluebackup.html#BackupAgent">Extending
+BackupAgent</a>.
\ No newline at end of file
diff --git a/docs/html/guide/topics/data/backup.jd b/docs/html/guide/topics/data/backup.jd
index 619c790..a688c6e 100644
--- a/docs/html/guide/topics/data/backup.jd
+++ b/docs/html/guide/topics/data/backup.jd
@@ -1,930 +1,34 @@
-page.title=Data Backup
+page.title=Backing up App Data to the Cloud
+page.tags=cloud,sync,backup
+
+startpage=true
+
 @jd:body
 
+<p>Users often invest significant time and effort creating data and setting
+preferences within apps. Preserving that data for users if they replace a broken
+device or upgrade to a new one is an important part of ensuring a great user
+experience. This section covers techniques for backing up data to the cloud so
+that users can restore their data.
 
-<div id="qv-wrapper">
-<div id="qv">
+<p>Android provides two ways for apps to backup their data to the cloud:
+<a href="{@docRoot}guide/topics/data/autobackup.html">Auto Backup for Apps</a> and
+<a href="{@docRoot}guide/topics/data/keyvaluebackup.html">Key/Value Backup</a>.
+Auto Backup, which is available starting API 23, preserves app data by uploading
+it to the user’s Google Drive account. The Key/Value Backup feature (formerly
+known as the Backup API and the Android Backup Service) preserves app data by
+uploading it to the <a href="{@docRoot}google/backup/index.html">Android Backup Service</a>.
 
-  <h2>Quickview</h2>
-  <ul>
-    <li>Back up the user's data to the cloud in case the user loses it</li>
-    <li>If the user upgrades to a new Android-powered device, your app can restore the user's
-data onto the new device</li>
-    <li>Easily back up SharedPreferences and private files with BackupAgentHelper</li>
-    <li>Requires API Level 8</li>
-  </ul>
+<p>Generally, we recommend Auto Backup because it requires no work to implement.
+Apps that target Android 6.0 (API level 23) or higher are automatically enabled
+for Auto Backup. The Auto Backup feature does have some limitations in terms of
+what data it can backup and it's availability on Android 6.0 and higher devices.
+Consider using the Key/Value Backup feature if you have more specific needs for
+backing up your app data. For more information, see <a href="{@docRoot}guide/topics/data/keyvaluebackup.html#Comparison">Comparison of Key/Value and Auto Backup</a></p>
 
-  <h2>In this document</h2>
-  <ol>
-    <li><a href="#Basics">The Basics</a></li>
-    <li><a href="#BackupManifest">Declaring the Backup Agent in Your Manifest</a></li>
-    <li><a href="#BackupKey">Registering for Android Backup Service</a></li>
-    <li><a href="#BackupAgent">Extending BackupAgent</a>
-      <ol>
-        <li><a href="#RequiredMethods">Required Methods</a></li>
-        <li><a href="#PerformingBackup">Performing backup</a></li>
-        <li><a href="#PerformingRestore">Performing restore</a></li>
-      </ol>
-    </li>
-    <li><a href="#BackupAgentHelper">Extending BackupAgentHelper</a>
-      <ol>
-        <li><a href="#SharedPreferences">Backing up SharedPreferences</a></li>
-        <li><a href="#Files">Backing up Private Files</a></li>
-      </ol>
-    </li>
-    <li><a href="#RestoreVersion">Checking the Restore Data Version</a></li>
-    <li><a href="#RequestingBackup">Requesting Backup</a></li>
-    <li><a href="#RequestingRestore">Requesting Restore</a></li>
-    <li><a href="#Testing">Testing Your Backup Agent</a></li>
-  </ol>
-
-  <h2>Key classes</h2>
-  <ol>
-    <li>{@link android.app.backup.BackupManager}</li>
-    <li>{@link android.app.backup.BackupAgent}</li>
-    <li>{@link android.app.backup.BackupAgentHelper}</li>
-  </ol>
-
-  <h2>See also</h2>
-  <ol>
-    <li><a href="{@docRoot}tools/help/bmgr.html">{@code bmgr} tool</a></li>
-  </ol>
-
-</div>
-</div>
-
-<p>Android's {@link android.app.backup backup} service allows you to copy your persistent
-application data to remote "cloud" storage, in order to provide a restore point for the
-application data and settings. If a user performs a factory reset or converts to a new
-Android-powered device, the system automatically restores your backup data when the application
-is re-installed. This way, your users don't need to reproduce their previous data or
-application settings. This process is completely transparent to the user and does not affect the
-functionality or user experience in your application.</p>
-
-<p>During a backup operation (which your application can request), Android's Backup Manager ({@link
-android.app.backup.BackupManager}) queries your application for backup data, then hands it to
-a backup transport, which then delivers the data to the cloud storage. During a
-restore operation, the Backup Manager retrieves the backup data from the backup transport and
-returns it to your application so your application can restore the data to the device. It's
-possible for your application to request a restore, but that shouldn't be necessary&mdash;Android
-automatically performs a restore operation when your application is installed and there exists
-backup data associated with the user. The primary scenario in which backup data is restored is when
-a user resets their device or upgrades to a new device and their previously installed
-applications are re-installed.</p>
-
-<p class="note"><strong>Note:</strong> The backup service is <em>not</em> designed for
-synchronizing application data with other clients or saving data that you'd like to access during
-the normal application lifecycle. You cannot read or write backup data on demand and cannot access
-it in any way other than through the APIs provided by the Backup Manager.</p>
-
-<p>The backup transport is the client-side component of Android's backup framework, which is
-customizable by
-the device manufacturer and service provider. The backup transport may differ from device to device
-and which backup transport is available on any given device is transparent to your application. The
-Backup Manager APIs isolate your application from the actual backup transport available on a given
-device&mdash;your application communicates with the Backup Manager through a fixed set of APIs,
-regardless of the underlying transport.</p>
-
-<p>Data backup is <em>not</em> guaranteed to be available on all Android-powered
-devices. However, your application is not adversely affected in the event
-that a device does not provide a backup transport. If you believe that users will benefit from data
-backup in your application, then you can implement it as described in this document, test it, then
-publish your application without any concern about which devices actually perform backup. When your
-application runs on a device that does not provide a backup transport, your application operates
-normally, but will not receive callbacks from the Backup Manager to backup data.</p>
-
-<p>Although you cannot know what the current transport is, you are always assured that your
-backup data cannot be read by other applications on the device. Only the Backup Manager and backup
-transport have access to the data you provide during a backup operation.</p>
-
-<p class="caution"><strong>Caution:</strong> Because the cloud storage and transport service can
-differ from device to device, Android makes no guarantees about the security of your data while
-using backup. You should always be cautious about using backup to store sensitive data, such as
-usernames and passwords.</p>
-
-
-<h2 id="Basics">The Basics</h2>
-
-<p>To backup your application data, you need to implement a backup agent. Your backup
-agent is called by the Backup Manager to provide the data you want to back up. It is also called
-to restore your backup data when the application is re-installed. The Backup Manager handles all
-your data transactions with the cloud storage (using the backup transport) and your backup agent
-handles all your data transactions on the device.</p>
-
-<p>To implement a backup agent, you must:</p>
-
-<ol>
-  <li>Declare your backup agent in your manifest file with the <a
-href="{@docRoot}guide/topics/manifest/application-element.html#agent">{@code
-android:backupAgent}</a> attribute.</li>
-  <li>Register your application with a backup service. Google offers <a
-href="http://code.google.com/android/backup/index.html">Android Backup Service</a> as a backup
-service for most Android-powered devices, which requires that you register your application in
-order for it to work. Any other backup services available might also require you to register
-in order to store your data on their servers.</li>
-  <li>Define a backup agent by either:</p>
-    <ol type="a">
-      <li><a href="#BackupAgent">Extending BackupAgent</a>
-        <p>The {@link android.app.backup.BackupAgent} class provides the central interface with
-which your application communicates with the Backup Manager. If you extend this class
-directly, you must override {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} and {@link
-android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
-onRestore()} to handle the backup and restore operations for your data.</p>
-        <p><em>Or</em></p>
-      <li><a href="#BackupAgentHelper">Extending BackupAgentHelper</a>
-        <p>The {@link android.app.backup.BackupAgentHelper} class provides a convenient
-wrapper around the {@link android.app.backup.BackupAgent} class, which minimizes the amount of code
-you need to write. In your {@link android.app.backup.BackupAgentHelper}, you must use one or more
-"helper" objects, which automatically backup and restore certain types of data, so that you do not
-need to implement {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} and {@link
-android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
-onRestore()}.</p>
-        <p>Android currently provides backup helpers that will backup and restore complete files
-from {@link android.content.SharedPreferences} and <a
-href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>.</p>
-      </li>
-    </ol>
-  </li>
-</ol>
-
-
-
-<h2 id="BackupManifest">Declaring the Backup Agent in Your Manifest</h2>
-
-<p>This is the easiest step, so once you've decided on the class name for your backup agent, declare
-it in your manifest with the <a
-href="{@docRoot}guide/topics/manifest/application-element.html#agent">{@code
-android:backupAgent}</a> attribute in the <a
-href="{@docRoot}guide/topics/manifest/application-element.html">{@code
-<application>}</a> tag.</p>
-
-<p>For example:</p>
-
-<pre>
-&lt;manifest ... &gt;
-    ...
-    &lt;application android:label="MyApplication"
-                 <b>android:backupAgent="MyBackupAgent"</b>&gt;
-        &lt;activity ... &gt;
-            ...
-        &lt;/activity&gt;
-    &lt;/application&gt;
-&lt;/manifest&gt;
-</pre>
-
-<p>Another attribute you might want to use is <a
-href="{@docRoot}guide/topics/manifest/application-element.html#restoreany">{@code
-android:restoreAnyVersion}</a>. This attribute takes a boolean value to indicate whether you
-want to restore the application data regardless of the current application version compared to the
-version that produced the backup data. (The default value is "{@code false}".) See <a
-href="#RestoreVersion">Checking the Restore Data Version</a> for more information.</p>
-
-<p class="note"><strong>Note:</strong> The backup service and the APIs you must use are
-available only on devices running API Level 8 (Android 2.2) or greater, so you should also
-set your <a
-href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code android:minSdkVersion}</a>
-attribute to "8".</p>
-
-
-
-
-<h2 id="BackupKey">Registering for Android Backup Service</h2>
-
-<p>Google provides a backup transport with <a
-href="http://code.google.com/android/backup/index.html">Android Backup Service</a> for most
-Android-powered devices running Android 2.2 or greater.</p>
-
-<p>In order for your application to perform backup using Android Backup Service, you must
-register your application with the service to receive a Backup Service Key, then
-declare the Backup Service Key in your Android manifest.</p>
-
-<p>To get your Backup Service Key, <a
-href="http://code.google.com/android/backup/signup.html">register for Android Backup Service</a>.
-When you register, you will be provided a Backup Service Key and the appropriate {@code
-<meta-data>} XML code for your Android manifest file, which you must include as a child of the
-{@code <application>} element. For example:</p>
-
-<pre>
-&lt;application android:label="MyApplication"
-             android:backupAgent="MyBackupAgent"&gt;
-    ...
-    &lt;meta-data android:name="com.google.android.backup.api_key"
-        android:value="AEdPqrEAAAAIDaYEVgU6DJnyJdBmU7KLH3kszDXLv_4DIsEIyQ" /&gt;
-&lt;/application&gt;
-</pre>
-
-<p>The <code>android:name</code> must be <code>"com.google.android.backup.api_key"</code> and
-the <code>android:value</code> must be the Backup Service Key received from the Android Backup
-Service registration.</p>
-
-<p>If you have multiple applications, you must register each one, using the respective package
-name.</p>
-
-<p class="note"><strong>Note:</strong> The backup transport provided by Android Backup Service is
-not guaranteed to be available
-on all Android-powered devices that support backup. Some devices might support backup
-using a different transport, some devices might not support backup at all, and there is no way for
-your application to know what transport is used on the device. However, if you implement backup for
-your application, you should always include a Backup Service Key for Android Backup Service so
-your application can perform backup when the device uses the Android Backup Service transport. If
-the device does not use Android Backup Service, then the {@code <meta-data>} element with the
-Backup Service Key is ignored.</p>
-
-
-
-
-<h2 id="BackupAgent">Extending BackupAgent</h2>
-
-<p>Most applications shouldn't need to extend the {@link android.app.backup.BackupAgent} class
-directly, but should instead <a href="#BackupAgentHelper">extend BackupAgentHelper</a> to take
-advantage of the built-in helper classes that automatically backup and restore your files. However,
-you might want to extend {@link android.app.backup.BackupAgent} directly if you need to:</p>
-<ul>
-  <li>Version your data format. For instance, if you anticipate the need to revise the
-format in which you write your application data, you can build a backup agent to cross-check your
-application version during a restore operation and perform any necessary compatibility work if the
-version on the device is different than that of the backup data. For more information, see <a
-href="#RestoreVersion">Checking the Restore Data Version</a>.</li>
-  <li>Instead of backing up an entire file, you can specify the portions of data the should be
-backed up and how each portion is then restored to the device. (This can also help you manage
-different versions, because you read and write your data as unique entities, rather than
-complete files.)</li>
-  <li>Back up data in a database. If you have an SQLite database that you want to restore when
-the user re-installs your application, you need to build a custom {@link
-android.app.backup.BackupAgent} that reads the appropriate data during a backup operation, then
-create your table and insert the data during a restore operation.</li>
-</ul>
-
-<p>If you don't need to perform any of the tasks above and want to back up complete files from
-{@link android.content.SharedPreferences} or <a
-href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>, you
-should skip to <a href="#BackupAgentHelper">Extending BackupAgentHelper</a>.</p>
-
-
-
-<h3 id="RequiredMethods">Required Methods</h3>
-
-<p>When you create a backup agent by extending {@link android.app.backup.BackupAgent}, you
-must implement the following callback methods:</p>
-
-<dl>
-  <dt>{@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()}</dt>
-    <dd>The Backup Manager calls this method after you <a href="#RequestingBackup">request a
-backup</a>. In this method, you read your application data from the device and pass the data you
-want to back up to the Backup Manager, as described below in <a href="#PerformingBackup">Performing
-backup</a>.</dd>
-
-  <dt>{@link
-android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
-onRestore()}</dt>
-    <dd>The Backup Manager calls this method during a restore operation (you can <a
-href="#RequestingRestore">request a restore</a>, but the system automatically performs restore when
-the user re-installs your application). When it calls this method, the Backup Manager delivers your
-backup data, which you then restore to the device, as described below in <a
-href="#PerformingRestore">Performing restore</a>.</dd>
-</dl>
-
-
-
-<h3 id="PerformingBackup">Performing backup</h3>
-
-
-<p>When it's time to back up your application data, the Backup Manager calls your {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} method. This is where you must provide your application data to the Backup Manager so
-it can be saved to cloud storage.</p>
-
-<p>Only the Backup Manager can call your backup agent's {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} method. Each time that your application data changes and you want to perform a backup,
-you must request a backup operation by calling {@link
-android.app.backup.BackupManager#dataChanged()} (see <a href="#RequestingBackup">Requesting
-Backup</a> for more information). A backup request does not result in an immediate call to your
-{@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} method. Instead, the Backup Manager waits for an appropriate time, then performs
-backup for all applications that have requested a backup since the last backup was performed.</p>
-
-<p class="note"><strong>Tip:</strong> While developing your application, you can initiate an
-immediate backup operation from the Backup Manager with the <a
-href="{@docRoot}tools/help/bmgr.html">{@code bmgr} tool</a>.</p>
-
-<p>When the Backup Manager calls your {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} method, it passes three parameters:</p>
-
-<dl>
-  <dt>{@code oldState}</dt>
-    <dd>An open, read-only {@link android.os.ParcelFileDescriptor} pointing to the last backup
-state provided by your application. This is not the backup data from cloud storage, but a
-local representation of the data that was backed up the last time {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} was called (as defined by {@code newState}, below, or from {@link
-android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
-onRestore()}&mdash;more about this in the next section). Because {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} does not allow you to read existing backup data in
-the cloud storage, you can use this local representation to determine whether your data has changed
-since the last backup.</dd>
-  <dt>{@code data}</dt>
-    <dd>A {@link android.app.backup.BackupDataOutput} object, which you use to deliver your backup
-data to the Backup Manager.</dd>
-  <dt>{@code newState}</dt>
-    <dd>An open, read/write {@link android.os.ParcelFileDescriptor} pointing to a file in which
-you must write a representation of the data that you delivered to {@code data} (a representation
-can be as simple as the last-modified timestamp for your file). This object is
-returned as {@code oldState} the next time the Backup Manager calls your {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} method. If you do not write your backup data to {@code newState}, then {@code oldState}
-will point to an empty file next time Backup Manager calls {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()}.</dd>
-</dl>
-
-<p>Using these parameters, you should implement your {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} method to do the following:</p>
-
-<ol>
-  <li>Check whether your data has changed since the last backup by comparing {@code oldState} to
-your current data. How you read data in {@code oldState} depends on how you originally wrote it to
-{@code newState} (see step 3). The easiest way to record the state of a file is with its
-last-modified timestamp. For example, here's how you can read and compare a timestamp from {@code
-oldState}:
-    <pre>
-// Get the oldState input stream
-FileInputStream instream = new FileInputStream(oldState.getFileDescriptor());
-DataInputStream in = new DataInputStream(instream);
-
-try {
-    // Get the last modified timestamp from the state file and data file
-    long stateModified = in.readLong();
-    long fileModified = mDataFile.lastModified();
-
-    if (stateModified != fileModified) {
-        // The file has been modified, so do a backup
-        // Or the time on the device changed, so be safe and do a backup
-    } else {
-        // Don't back up because the file hasn't changed
-        return;
-    }
-} catch (IOException e) {
-    // Unable to read state file... be safe and do a backup
-}
-</pre>
-    <p>If nothing has changed and you don't need to back up, skip to step 3.</p>
-  </li>
-  <li>If your data has changed, compared to {@code oldState}, write the current data to
-{@code data} to back it up to the cloud storage.
-    <p>You must write each chunk of data as an "entity" in the {@link
-android.app.backup.BackupDataOutput}. An entity is a flattened binary data
-record that is identified by a unique key string. Thus, the data set that you back up is
-conceptually a set of key-value pairs.</p>
-    <p>To add an entity to your backup data set, you must:</p>
-    <ol>
-      <li>Call {@link android.app.backup.BackupDataOutput#writeEntityHeader(String,int)
-writeEntityHeader()}, passing a unique string key for the data you're about to write and the data
-size.</li>
-      <li>Call {@link android.app.backup.BackupDataOutput#writeEntityData(byte[],int)
-writeEntityData()}, passing a byte buffer that contains your data and the number of bytes to write
-from the buffer (which should match the size passed to {@link
-android.app.backup.BackupDataOutput#writeEntityHeader(String,int) writeEntityHeader()}).</li>
-    </ol>
-    <p>For example, the following code flattens some data into a byte stream and writes it into a
-single entity:</p>
-    <pre>
-// Create buffer stream and data output stream for our data
-ByteArrayOutputStream bufStream = new ByteArrayOutputStream();
-DataOutputStream outWriter = new DataOutputStream(bufStream);
-// Write structured data
-outWriter.writeUTF(mPlayerName);
-outWriter.writeInt(mPlayerScore);
-// Send the data to the Backup Manager via the BackupDataOutput
-byte[] buffer = bufStream.toByteArray();
-int len = buffer.length;
-data.writeEntityHeader(TOPSCORE_BACKUP_KEY, len);
-data.writeEntityData(buffer, len);
-</pre>
-    <p>Perform this for each piece of data that you want to back up. How you divide your data into
-entities is up to you (and you might use just one entity).</p>
-  </li>
-  <li>Whether or not you perform a backup (in step 2), write a representation of the current data to
-the {@code newState} {@link android.os.ParcelFileDescriptor}. The Backup Manager retains this object
-locally as a representation of the data that is currently backed up. It passes this back to you as
-{@code oldState} the next time it calls {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} so you can determine whether another backup is necessary (as handled in step 1). If you
-do not write the current data state to this file, then
-{@code oldState} will be empty during the next callback.
-    <p>The following example saves a representation of the current data into {@code newState} using
-the file's last-modified timestamp:</p>
-    <pre>
-FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor());
-DataOutputStream out = new DataOutputStream(outstream);
-
-long modified = mDataFile.lastModified();
-out.writeLong(modified);
-</pre>
-  </li>
-</ol>
-
-<p class="caution"><strong>Caution:</strong> If your application data is saved to a file, make sure
-that you use synchronized statements while accessing the file so that your backup agent does not
-read the file while an Activity in your application is also writing the file.</p>
-
-
-
-
-<h3 id="PerformingRestore">Performing restore</h3>
-
-<p>When it's time to restore your application data, the Backup Manager calls your backup
-agent's {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
-onRestore()} method. When it calls this method, the Backup Manager delivers your backup data so
-you can restore it onto the device.</p>
-
-<p>Only the Backup Manager can call {@link
-android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
-onRestore()}, which happens automatically when the system installs your application and
-finds existing backup data. However, you can request a restore operation for
-your application by calling {@link
-android.app.backup.BackupManager#requestRestore(RestoreObserver) requestRestore()} (see <a
-href="#RequestingRestore">Requesting restore</a> for more information).</p>
-
-<p class="note"><strong>Note:</strong> While developing your application, you can also request a
-restore operation with the <a href="{@docRoot}tools/help/bmgr.html">{@code bmgr}
-tool</a>.</p>
-
-<p>When the Backup Manager calls your {@link
-android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
-onRestore()} method, it passes three parameters:</p>
-
-<dl>
-  <dt>{@code data}</dt>
-    <dd>A {@link android.app.backup.BackupDataInput}, which allows you to read your backup
-data.</dd>
-  <dt>{@code appVersionCode}</dt>
-    <dd>An integer representing the value of your application's <a
-href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code android:versionCode}</a>
-manifest attribute, as it was when this data was backed up. You can use this to cross-check the
-current application version and determine if the data format is compatible. For more
-information about using this to handle different versions of restore data, see the section
-below about <a href="#RestoreVersion">Checking the Restore Data Version</a>.</dd>
-  <dt>{@code newState}</dt>
-    <dd>An open, read/write {@link android.os.ParcelFileDescriptor} pointing to a file in which
-you must write the final backup state that was provided with {@code data}. This object is
-returned as {@code oldState} the next time {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} is called. Recall that you must also write the same {@code newState} object in the
-{@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} callback&mdash;also doing it here ensures that the {@code oldState} object given to
-{@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} is valid even the first time {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} is called after the device is restored.</dd>
-</dl>
-
-<p>In your implementation of {@link
-android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
-onRestore()}, you should call {@link android.app.backup.BackupDataInput#readNextHeader()} on the
-{@code data} to iterate
-through all entities in the data set. For each entity found, do the following:</p>
-
-<ol>
-  <li>Get the entity key with {@link android.app.backup.BackupDataInput#getKey()}.</li>
-  <li>Compare the entity key to a list of known key values that you should have declared as static
-final strings inside your {@link android.app.backup.BackupAgent} class. When the key matches one of
-your known key strings, enter into a statement to extract the entity data and save it to the device:
-    <ol>
-      <li>Get the entity data size with {@link
-android.app.backup.BackupDataInput#getDataSize()} and create a byte array of that size.</li>
-      <li>Call {@link android.app.backup.BackupDataInput#readEntityData(byte[],int,int)
-readEntityData()} and pass it the byte array, which is where the data will go, and specify the
-start offset and the size to read.</li>
-      <li>Your byte array is now full and you can read the data and write it to the device
-however you like.</li>
-    </ol>
-  </li>
-  <li>After you read and write your data back to the device, write the state of your data to the
-{@code newState} parameter the same as you do during {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()}.
-</ol>
-
-<p>For example, here's how you can restore the data backed up by the example in the previous
-section:</p>
-
-<pre>
-&#64;Override
-public void onRestore(BackupDataInput data, int appVersionCode,
-                      ParcelFileDescriptor newState) throws IOException {
-    // There should be only one entity, but the safest
-    // way to consume it is using a while loop
-    while (data.readNextHeader()) {
-        String key = data.getKey();
-        int dataSize = data.getDataSize();
-
-        // If the key is ours (for saving top score). Note this key was used when
-        // we wrote the backup entity header
-        if (TOPSCORE_BACKUP_KEY.equals(key)) {
-            // Create an input stream for the BackupDataInput
-            byte[] dataBuf = new byte[dataSize];
-            data.readEntityData(dataBuf, 0, dataSize);
-            ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf);
-            DataInputStream in = new DataInputStream(baStream);
-
-            // Read the player name and score from the backup data
-            mPlayerName = in.readUTF();
-            mPlayerScore = in.readInt();
-
-            // Record the score on the device (to a file or something)
-            recordScore(mPlayerName, mPlayerScore);
-        } else {
-            // We don't know this entity key. Skip it. (Shouldn't happen.)
-            data.skipEntityData();
-        }
-    }
-
-    // Finally, write to the state blob (newState) that describes the restored data
-    FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor());
-    DataOutputStream out = new DataOutputStream(outstream);
-    out.writeUTF(mPlayerName);
-    out.writeInt(mPlayerScore);
-}
-</pre>
-
-<p>In this example, the {@code appVersionCode} parameter passed to {@link
-android.app.backup.BackupAgent#onRestore onRestore()} is not used. However, you might want to use
-it if you've chosen to perform backup when the user's version of the application has actually moved
-backward (for example, the user went from version 1.5 of your app to 1.0). For more information, see
-the section about <a href="#RestoreVersion">Checking the Restore Data Version</a>.</p>
-
-<div class="special">
-<p>For an example implementation of {@link android.app.backup.BackupAgent}, see the <a
-href="{@docRoot}resources/samples/BackupRestore/src/com/example/android/backuprestore/ExampleAgent.html">{@code
-ExampleAgent}</a> class in the <a
-href="{@docRoot}resources/samples/BackupRestore/index.html">Backup and Restore</a> sample
-application.</p>
-</div>
-
-
-
-
-
-
-<h2 id="BackupAgentHelper">Extending BackupAgentHelper</h2>
-
-<p>You should build your backup agent using {@link android.app.backup.BackupAgentHelper} if you want
-to back up complete files (from either {@link android.content.SharedPreferences} or <a
-href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>).
-Building your backup agent with {@link android.app.backup.BackupAgentHelper} requires far less
-code than extending {@link android.app.backup.BackupAgent}, because you don't have to implement
-{@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} and {@link
-android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
-onRestore()}.</p>
-
-<p>Your implementation of {@link android.app.backup.BackupAgentHelper} must
-use one or more backup helpers. A backup helper is a specialized
-component that {@link android.app.backup.BackupAgentHelper} summons to perform backup and
-restore operations for a particular type of data. The Android framework currently provides two
-different helpers:</p>
-<ul>
-  <li>{@link android.app.backup.SharedPreferencesBackupHelper} to backup {@link
-android.content.SharedPreferences} files.</li>
-  <li>{@link android.app.backup.FileBackupHelper} to backup files from <a
-href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>.</li>
-</ul>
-
-<p>You can include multiple helpers in your {@link android.app.backup.BackupAgentHelper}, but only
-one helper is needed for each data type. That is, if you have multiple {@link
-android.content.SharedPreferences} files, then you need only one {@link
-android.app.backup.SharedPreferencesBackupHelper}.</p>
-
-<p>For each helper you want to add to your {@link android.app.backup.BackupAgentHelper}, you must do
-the following during your {@link android.app.backup.BackupAgent#onCreate()} method:</p>
-<ol>
-  <li>Instantiate in instance of the desired helper class. In the class constructor, you must
-specify the appropriate file(s) you want to backup.</li>
-  <li>Call {@link android.app.backup.BackupAgentHelper#addHelper(String,BackupHelper) addHelper()}
-to add the helper to your {@link android.app.backup.BackupAgentHelper}.</li>
-</ol>
-
-<p>The following sections describe how to create a backup agent using each of the available
-helpers.</p>
-
-
-
-<h3 id="SharedPreferences">Backing up SharedPreferences</h3>
-
-<p>When you instantiate a {@link android.app.backup.SharedPreferencesBackupHelper}, you must
-include the name of one or more {@link android.content.SharedPreferences} files.</p>
-
-<p>For example, to back up a {@link android.content.SharedPreferences} file named
-"user_preferences", a complete backup agent using {@link android.app.backup.BackupAgentHelper} looks
-like this:</p>
-
-<pre>
-public class MyPrefsBackupAgent extends BackupAgentHelper {
-    // The name of the SharedPreferences file
-    static final String PREFS = "user_preferences";
-
-    // A key to uniquely identify the set of backup data
-    static final String PREFS_BACKUP_KEY = "prefs";
-
-    // Allocate a helper and add it to the backup agent
-    &#64;Override
-    public void onCreate() {
-        SharedPreferencesBackupHelper helper =
-                new SharedPreferencesBackupHelper(this, PREFS);
-        addHelper(PREFS_BACKUP_KEY, helper);
-    }
-}
-</pre>
-
-<p>That's it! That's your entire backup agent. The {@link
-android.app.backup.SharedPreferencesBackupHelper} includes all the code
-needed to backup and restore a {@link android.content.SharedPreferences} file.</p>
-
-<p>When the Backup Manager calls {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} and {@link
-android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
-onRestore()}, {@link android.app.backup.BackupAgentHelper} calls your backup helpers to perform
-backup and restore for your specified files.</p>
-
-<p class="note"><strong>Note:</strong> {@link android.content.SharedPreferences} are threadsafe, so
-you can safely read and write the shared preferences file from your backup agent and
-other activities.</p>
-
-
-
-<h3 id="Files">Backing up other files</h3>
-
-<p>When you instantiate a {@link android.app.backup.FileBackupHelper}, you must include the name of
-one or more files that are saved to your application's <a
-href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>
-(as specified by {@link android.content.ContextWrapper#getFilesDir()}, which is the same
-location where {@link android.content.Context#openFileOutput(String,int) openFileOutput()} writes
-files).</p>
-
-<p>For example, to backup two files named "scores" and "stats," a backup agent using {@link
-android.app.backup.BackupAgentHelper} looks like this:</p>
-
-<pre>
-public class MyFileBackupAgent extends BackupAgentHelper {
-    // The name of the file
-    static final String TOP_SCORES = "scores";
-    static final String PLAYER_STATS = "stats";
-
-    // A key to uniquely identify the set of backup data
-    static final String FILES_BACKUP_KEY = "myfiles";
-
-    // Allocate a helper and add it to the backup agent
-    &#64;Override
-    public void onCreate() {
-        FileBackupHelper helper = new FileBackupHelper(this,
-                TOP_SCORES, PLAYER_STATS);
-        addHelper(FILES_BACKUP_KEY, helper);
-    }
-}
-</pre>
-
-<p>The {@link android.app.backup.FileBackupHelper} includes all the code necessary to backup and
-restore files that are saved to your application's <a
-href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>..</p>
-
-<p>However, reading and writing to files on internal storage is <strong>not threadsafe</strong>. To
-ensure that your backup agent does not read or write your files at the same time as your activities,
-you must use synchronized statements each time you perform a read or write. For example,
-in any Activity where you read and write the file, you need an object to use as the intrinsic
-lock for the synchronized statements:</p>
-
-<pre>
-// Object for intrinsic lock
-static final Object sDataLock = new Object();
-</pre>
-
-<p>Then create a synchronized statement with this lock each time you read or write the files. For
-example, here's a synchronized statement for writing the latest score in a game to a file:</p>
-
-<pre>
-try {
-    synchronized (MyActivity.sDataLock) {
-        File dataFile = new File({@link android.content.Context#getFilesDir()}, TOP_SCORES);
-        RandomAccessFile raFile = new RandomAccessFile(dataFile, "rw");
-        raFile.writeInt(score);
-    }
-} catch (IOException e) {
-    Log.e(TAG, "Unable to write to file");
-}
-</pre>
-
-<p>You should synchronize your read statements with the same lock.</p>
-
-<p>Then, in your {@link android.app.backup.BackupAgentHelper}, you must override {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} and {@link
-android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
-onRestore()} to synchronize the backup and restore operations with the same
-intrinsic lock. For example, the {@code MyFileBackupAgent} example from above needs the following
-methods:</p>
-
-<pre>
-&#64;Override
-public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
-          ParcelFileDescriptor newState) throws IOException {
-    // Hold the lock while the FileBackupHelper performs backup
-    synchronized (MyActivity.sDataLock) {
-        super.onBackup(oldState, data, newState);
-    }
-}
-
-&#64;Override
-public void onRestore(BackupDataInput data, int appVersionCode,
-        ParcelFileDescriptor newState) throws IOException {
-    // Hold the lock while the FileBackupHelper restores the file
-    synchronized (MyActivity.sDataLock) {
-        super.onRestore(data, appVersionCode, newState);
-    }
-}
-</pre>
-
-<p>That's it. All you need to do is add your {@link android.app.backup.FileBackupHelper} in the
-{@link android.app.backup.BackupAgent#onCreate()} method and override {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} and {@link
-android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
-onRestore()} to synchronize read and write operations.</p>
-
-<div class="special">
-<p>For an example implementation of {@link
-android.app.backup.BackupAgentHelper} with {@link android.app.backup.FileBackupHelper}, see the
-{@code FileHelperExampleAgent} class in the <a
-href="{@docRoot}resources/samples/BackupRestore/index.html">Backup and Restore</a> sample
-application.</p>
-</div>
-
-
-
-
-
-
-<h2 id="RestoreVersion">Checking the Restore Data Version</h2>
-
-<p>When the Backup Manager saves your data to cloud storage, it automatically includes the version
-of your application, as defined by your manifest file's <a
-href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code android:versionCode}</a>
-attribute. Before the Backup Manager calls your backup agent to restore your data, it
-looks at the <a
-href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code
-android:versionCode}</a> of the installed application and compares it to the value
-recorded in the restore data set. If the version recorded in the restore data set is
-<em>newer</em> than the application version on the device, then the user has downgraded their
-application. In this case, the Backup Manager will abort the restore operation for your application
-and not call your {@link
-android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}
-method, because the restore set is considered meaningless to an older version.</p>
-
-<p>You can override this behavior with the <a
-href="{@docRoot}guide/topics/manifest/application-element.html#restoreany">{@code
-android:restoreAnyVersion}</a> attribute. This attribute is either "{@code true}" or "{@code
-false}" to indicate whether you want to restore the application regardless of the restore set
-version. The default value is "{@code false}". If you define this to be "{@code true}" then the
-Backup Manager will ignore the <a
-href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code android:versionCode}</a>
-and call your {@link
-android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}
-method in all cases. In doing so, you can manually check for the version difference in your {@link
-android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}
-method and take any steps necessary to make the data compatible if the versions conflict.</p>
-
-<p>To help you handle different versions during a restore operation, the {@link
-android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}
-method passes you the version code included with the restore data set as the {@code appVersionCode}
-parameter. You can then query the current application's version code with the {@link
-android.content.pm.PackageInfo#versionCode PackageInfo.versionCode} field. For example:</p>
-
-<pre>
-PackageInfo info;
-try {
-    String name = {@link android.content.ContextWrapper#getPackageName() getPackageName}();
-    info = {@link android.content.ContextWrapper#getPackageManager
-getPackageManager}().{@link android.content.pm.PackageManager#getPackageInfo(String,int)
-getPackageInfo}(name,0);
-} catch (NameNotFoundException nnfe) {
-    info = null;
-}
-
-int version;
-if (info != null) {
-    version = info.versionCode;
-}
-</pre>
-
-<p>Then simply compare the {@code version} acquired from {@link android.content.pm.PackageInfo}
-to the {@code appVersionCode} passed into {@link
-android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}.
-</p>
-
-<p class="caution"><strong>Caution:</strong> Be certain you understand the consequences of setting
-<a href="{@docRoot}guide/topics/manifest/application-element.html#restoreany">{@code
-android:restoreAnyVersion}</a> to "{@code true}" for your application. If each version of your
-application that supports backup does not properly account for variations in your data format during
-{@link
-android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()},
-then the data on the device could be saved in a format incompatible with the version currently
-installed on the device.</p>
-
-
-
-<h2 id="RequestingBackup">Requesting Backup</h2>
-
-<p>You can request a backup operation at any time by calling {@link
-android.app.backup.BackupManager#dataChanged()}. This method notifies the Backup Manager that you'd
-like to backup your data using your backup agent. The Backup Manager then calls your backup
-agent's {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()} method at an opportune time in the future. Typically, you should
-request a backup each time your data changes (such as when the user changes an application
-preference that you'd like to back up). If you call {@link
-android.app.backup.BackupManager#dataChanged()} several times consecutively, before the Backup
-Manager requests a backup from your agent, your agent still receives just one call to {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
-onBackup()}.</p>
-
-<p class="note"><strong>Note:</strong> While developing your application, you can request a
-backup and initiate an immediate backup operation with the <a
-href="{@docRoot}tools/help/bmgr.html">{@code bmgr}
-tool</a>.</p>
-
-
-<h2 id="RequestingRestore">Requesting Restore</h2>
-
-<p>During the normal life of your application, you shouldn't need to request a restore operation.
-They system automatically checks for backup data and performs a restore when your application is
-installed. However, you can manually request a restore operation by calling {@link
-android.app.backup.BackupManager#requestRestore(RestoreObserver) requestRestore()}, if necessary. In
-which case, the Backup Manager calls your {@link
-android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}
-implementation, passing the data from the current set of backup data.</p>
-
-<p class="note"><strong>Note:</strong> While developing your application, you can request a
-restore operation with the <a href="{@docRoot}tools/help/bmgr.html">{@code bmgr}
-tool</a>.</p>
-
-
-<h2 id="Testing">Testing Your Backup Agent</h2>
-
-<p>Once you've implemented your backup agent, you can test the backup and restore functionality
-with the following procedure, using <a
-href="{@docRoot}tools/help/bmgr.html">{@code bmgr}</a>.</p>
-
-<ol>
-  <li>Install your application on a suitable Android system image
-    <ul>
-      <li>If using the emulator, create and use an AVD with Android 2.2 (API Level 8).</li>
-      <li>If using a device, the device must be running Android 2.2 or greater and have Google
-Play built in.</li>
-    </ul>
-  </li>
-  <li>Ensure that backup is enabled
-    <ul>
-      <li>If using the emulator, you can enable backup with the following command from your SDK
-{@code tools/} path:
-<pre class="no-pretty-print">adb shell bmgr enable true</pre>
-      </li>
-      <li>If using a device, open the system <b>Settings</b>, select
-      <b>Backup & reset</b>, then enable
-      <b>Back up my data</b> and <b>Automatic restore</b>.</li>
-    </ul>
-  </li>
-  <li>Open your application and initialize some data
-    <p>If you've properly implemented backup in your application, then it should request a
-backup each time the data changes. For example, each time the user changes some data, your app
-should call {@link android.app.backup.BackupManager#dataChanged()}, which adds a backup request to
-the Backup Manager queue. For testing purposes, you can also make a request with the following
-{@code bmgr} command:</p>
-<pre class="no-pretty-print">adb shell bmgr backup <em>your.package.name</em></pre>
-  </li>
-  <li>Initiate a backup operation:
-<pre class="no-pretty-print">adb shell bmgr run</pre>
-    <p>This forces the Backup Manager to perform all backup requests that are in its
-queue.</p>
-  <li>Uninstall your application:
-<pre class="no-pretty-print">adb uninstall <em>your.package.name</em></pre>
-  </li>
-  <li>Re-install your application.</li>
-</ol>
-
-<p>If your backup agent is successful, all the data you initialized in step 4 is restored.</p>
-
-
+<p class="note"><strong>Note:</strong> These data backup features are not designed for synchronizing app data with other clients or
+saving data that you'd like to access during the normal application lifecycle.
+You cannot read or write backup data on demand. For synchronizing app data, see
+<a href="{@docRoot}training/sync-adapters/index.html">Transferring
+Data Using Sync Adapters</a> or <a href="https://developers.google.com/drive/android/">Google Drive Android
+API</a>.
\ No newline at end of file
diff --git a/docs/html/guide/topics/data/images/backup-framework.png b/docs/html/guide/topics/data/images/backup-framework.png
new file mode 100644
index 0000000..2ba2e612
--- /dev/null
+++ b/docs/html/guide/topics/data/images/backup-framework.png
Binary files differ
diff --git a/docs/html/guide/topics/data/index.jd b/docs/html/guide/topics/data/index.jd
index 3872825..2365f18 100644
--- a/docs/html/guide/topics/data/index.jd
+++ b/docs/html/guide/topics/data/index.jd
@@ -5,21 +5,3 @@
 
 @jd:body
 
-<div class="landing-docs">
-
-
-  <div class="col-12">
-    <h3>Training</h3>
-
-    <a href="{@docRoot}training/backup/index.html">
-      <h4>Backing up App Data to the Cloud</h4>
-      <p>
-        This class covers techniques for backing up data to the cloud so that
-        users can restore their data when recovering from a data loss (such as a
-        factory reset) or installing your application on a new device.
-      </p>
-    </a>
-
-  </div>
-
-</div>
diff --git a/docs/html/guide/topics/data/keyvaluebackup.jd b/docs/html/guide/topics/data/keyvaluebackup.jd
new file mode 100644
index 0000000..c7c5e2f
--- /dev/null
+++ b/docs/html/guide/topics/data/keyvaluebackup.jd
@@ -0,0 +1,884 @@
+page.title=Key/Value Backup
+page.tags=backup, marshmallow, androidm
+page.keywords=backup, kvbackup
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#Comparison">Comparison to Auto Backup</a></li>
+    <li><a href="#ImplementingBackup">Implementing Key/Value Backup</a></li>
+
+    <li><a href="#BackupManifest">Declaring the backup agent in your manifest</a></li>
+    <li><a href="#BackupKey">Registering for Android Backup Service</a></li>
+    <li><a href="#BackupAgent">Extending BackupAgent</a>
+      <ol>
+        <li><a href="#RequiredMethods">Required methods</a></li>
+        <li><a href="#PerformingBackup">Performing backup</a></li>
+        <li><a href="#PerformingRestore">Performing restore</a></li>
+      </ol>
+    </li>
+    <li><a href="#BackupAgentHelper">Extending BackupAgentHelper</a>
+      <ol>
+        <li><a href="#SharedPreferences">Backing up SharedPreferences</a></li>
+        <li><a href="#Files">Backing up private files</a></li>
+      </ol>
+    </li>
+    <li><a href="#RestoreVersion">Checking the restore data version</a></li>
+    <li><a href="#RequestingBackup">Requesting backup</a></li>
+    <li><a href="#RequestingRestore">Requesting restore</a></li>
+    <li><a href="#Migrating">Migrating to Auto Backup</a></li>
+  </ol>
+
+  <h2>Key classes</h2>
+  <ol>
+    <li>{@link android.app.backup.BackupManager}</li>
+    <li>{@link android.app.backup.BackupAgent}</li>
+    <li>{@link android.app.backup.BackupAgentHelper}</li>
+  </ol>
+
+</div>
+</div>
+
+
+<p>Since Android 2.2 (API 8), Android has offered the <em>Key/Value Backup</em>
+feature as a way for developers to backup app data to the cloud. The Key/Value
+Backup feature (formerly known as the Backup API and the Android Backup Service)
+preserves app data by uploading it to
+<a href="{@docRoot}google/backup/index.html">Android Backup Service</a>.
+The amount of data is limited to 5MB per user of your app and there is
+no charge for storing backup data.
+
+<p class="note"><strong>Note:</strong> If your app implements Key/Value Backup
+and targets API 23 or higher, you should set
+<a href="{@docRoot}reference/android/R.attr.html#fullBackupOnly">android:fullBackupOnly</a>.
+This attribute indicates whether or not to use Auto Backup on devices where it is available.
+
+<h2 id="Comparison">Comparison to Auto Backup</h2>
+<p>Like Auto Backup, Key/Value Backups are restored automatically whenever the app
+is installed. The following table describes some of the key differences between Key/Value Backup and Auto Backup:
+
+<table>
+  <tr>
+   <th>Key/Value Backup</th>
+   <th>Auto Backup</th>
+  </tr>
+  <tr>
+   <td>Available in API 8, Android 2.2</td>
+   <td>Available in API 23, Android 6.0</td>
+  </tr>
+  <tr>
+   <td>Apps must implement a {@link android.app.backup.BackupAgent}. The backup agent defines what data to backup and how to
+restore data.</td>
+   <td>By default, Auto Backup includes almost all of the app's files. You can
+use XML to include and exclude files. Under the hood, Auto Backup relies on a
+backup agent that is built into the framework.</td>
+  </tr>
+  <tr>
+   <td>Apps must issue a request when there is data
+that is ready to be backed up. Requests
+from multiple apps are batched and executed every few hours.</td>
+   <td>Backups happen automatically roughly once a day.</td>
+  </tr>
+  <tr>
+   <td>Backup data can be transmitted via wifi or cellular data.</td>
+   <td>Backup data is tranmitted only via wifi. If the device is never connected to a
+wifi network, then Auto Backup never occurs.</td>
+  </tr>
+  <tr>
+   <td>Apps are not shut down during backup.</td>
+   <td>The system shuts down the app during backup.</td>
+  </tr>
+  <tr>
+   <td>Backup data is stored in <a href="{@docRoot}google/backup/index.html">Android Backup Service</a> limited to 5MB per app.</td>
+   <td>Backup data is stored in the user's Google Drive limited to 25MB per app.</td>
+  </tr>
+  <tr>
+   <td>Related API methods are not filed based:<ul>
+      <li>{@link android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput, ParcelFileDescriptor) onBackup()}
+      <li>{@link android.app.backup.BackupAgent#onRestore(BackupDataInput, int, ParcelFileDescriptor) onRestore()}</ul></td>
+   <td>Related API methods are filed based:<ul>
+<li>{@link android.app.backup.BackupAgent#onFullBackup(FullBackupDataOutput) onFullBackup()}
+<li>{@link android.app.backup.BackupAgent#onRestoreFile(ParcelFileDescriptor,long,File,int,long,long) onRestoreFile()}</ul></td>
+  </tr>
+</table>
+
+<h2 id="ImplementingBackup">Implementing Key/Value Backup</h2>
+<p>To backup your application data, you need to implement a backup agent. Your backup
+agent is called by the Backup Manager both during backup and restore.</p>
+
+<p>To implement a backup agent, you must:</p>
+
+<ol>
+  <li>Declare your backup agent in your manifest file with the <a
+href="{@docRoot}guide/topics/manifest/application-element.html#agent">{@code
+android:backupAgent}</a> attribute.</li>
+  <li>Register your application with <a href="{@docRoot}google/backup/index.html">Android
+   Backup Service</a></li>
+  <li>Define a backup agent by either:</p>
+    <ol type="a">
+      <li><a href="#BackupAgent">Extending BackupAgent</a>
+        <p>The {@link android.app.backup.BackupAgent} class provides the central interface with
+which your application communicates with the Backup Manager. If you extend this class
+directly, you must override {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} and {@link
+android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
+onRestore()} to handle the backup and restore operations for your data.</p>
+        <p><em>Or</em></p>
+      <li><a href="#BackupAgentHelper">Extending BackupAgentHelper</a>
+        <p>The {@link android.app.backup.BackupAgentHelper} class provides a convenient
+wrapper around the {@link android.app.backup.BackupAgent} class, which minimizes the amount of code
+you need to write. In your {@link android.app.backup.BackupAgentHelper}, you must use one or more
+"helper" objects, which automatically backup and restore certain types of data, so that you do not
+need to implement {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} and {@link
+android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
+onRestore()}.</p>
+        <p>Android currently provides backup helpers that will backup and restore complete files
+from {@link android.content.SharedPreferences} and <a
+href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>.</p>
+      </li>
+    </ol>
+  </li>
+</ol>
+
+<h2 id="BackupManifest">Declaring the backup agent in your manifest</h2>
+
+<p>This is the easiest step, so once you've decided on the class name for your backup agent, declare
+it in your manifest with the <a
+href="{@docRoot}guide/topics/manifest/application-element.html#agent">{@code
+android:backupAgent}</a> attribute in the <a
+href="{@docRoot}guide/topics/manifest/application-element.html">{@code
+<application>}</a> tag.</p>
+
+<p>For example:</p>
+
+<pre>
+&lt;manifest ... &gt;
+    ...
+    &lt;application android:label="MyApplication"
+                 <b>android:backupAgent="MyBackupAgent"</b>&gt;
+        &lt;activity ... &gt;
+            ...
+        &lt;/activity&gt;
+    &lt;/application&gt;
+&lt;/manifest&gt;
+</pre>
+
+<p>Another attribute you might want to use is <a
+href="{@docRoot}guide/topics/manifest/application-element.html#restoreany">{@code
+android:restoreAnyVersion}</a>. This attribute takes a boolean value to indicate whether you
+want to restore the application data regardless of the current application version compared to the
+version that produced the backup data. (The default value is "{@code false}".) See <a
+href="#RestoreVersion">Checking the Restore Data Version</a> for more information.</p>
+
+<p class="note"><strong>Note:</strong> The backup service and the APIs you must use are
+available only on devices running API Level 8 (Android 2.2) or greater, so you should also
+set your <a
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code android:minSdkVersion}</a>
+attribute to "8".</p>
+
+
+
+
+<h2 id="BackupKey">Registering for Android Backup Service</h2>
+
+<p>Google provides a backup transport with <a
+href="{@docRoot}google/backup/index.html">Android Backup Service</a> for most
+Android-powered devices running Android 2.2 or greater.</p>
+
+<p>In order for your application to perform backup using Android Backup Service, you must
+register your application with the service to receive a Backup Service Key, then
+declare the Backup Service Key in your Android manifest.</p>
+
+<p>To get your Backup Service Key, <a
+href="{@docRoot}google/backup/signup.html">register for Android Backup Service</a>.
+When you register, you will be provided a Backup Service Key and the appropriate {@code
+<meta-data>} XML code for your Android manifest file, which you must include as a child of the
+{@code <application>} element. For example:</p>
+
+<pre>
+&lt;application android:label="MyApplication"
+             android:backupAgent="MyBackupAgent"&gt;
+    ...
+    &lt;meta-data android:name="com.google.android.backup.api_key"
+        android:value="AEdPqrEAAAAIDaYEVgU6DJnyJdBmU7KLH3kszDXLv_4DIsEIyQ" /&gt;
+&lt;/application&gt;
+</pre>
+
+<p>The <code>android:name</code> must be <code>"com.google.android.backup.api_key"</code> and
+the <code>android:value</code> must be the Backup Service Key received from the Android Backup
+Service registration.</p>
+
+<p>If you have multiple applications, you must register each one, using the respective package
+name.</p>
+
+<p class="note"><strong>Note:</strong> The backup transport provided by Android Backup Service is
+not guaranteed to be available
+on all Android-powered devices that support backup. Some devices might support backup
+using a different transport, some devices might not support backup at all, and there is no way for
+your application to know what transport is used on the device. However, if you implement backup for
+your application, you should always include a Backup Service Key for Android Backup Service so
+your application can perform backup when the device uses the Android Backup Service transport. If
+the device does not use Android Backup Service, then the {@code <meta-data>} element with the
+Backup Service Key is ignored.</p>
+
+
+
+
+<h2 id="BackupAgent">Extending BackupAgent</h2>
+
+<p>Most applications shouldn't need to extend the {@link android.app.backup.BackupAgent} class
+directly, but should instead <a href="#BackupAgentHelper">extend BackupAgentHelper</a> to take
+advantage of the built-in helper classes that automatically backup and restore your files. However,
+you might want to extend {@link android.app.backup.BackupAgent} directly if you need to:</p>
+<ul>
+  <li>Version your data format. For instance, if you anticipate the need to revise the
+format in which you write your application data, you can build a backup agent to cross-check your
+application version during a restore operation and perform any necessary compatibility work if the
+version on the device is different than that of the backup data. For more information, see <a
+href="#RestoreVersion">Checking the Restore Data Version</a>.</li>
+  <li>Instead of backing up an entire file, you can specify the portions of data the should be
+backed up and how each portion is then restored to the device. (This can also help you manage
+different versions, because you read and write your data as unique entities, rather than
+complete files.)</li>
+  <li>Back up data in a database. If you have an SQLite database that you want to restore when
+the user re-installs your application, you need to build a custom {@link
+android.app.backup.BackupAgent} that reads the appropriate data during a backup operation, then
+create your table and insert the data during a restore operation.</li>
+</ul>
+
+<p>If you don't need to perform any of the tasks above and want to back up complete files from
+{@link android.content.SharedPreferences} or <a
+href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>, you
+should skip to <a href="#BackupAgentHelper">Extending BackupAgentHelper</a>.</p>
+
+
+
+<h3 id="RequiredMethods">Required methods</h3>
+
+<p>When you create a backup agent by extending {@link android.app.backup.BackupAgent}, you
+must implement the following callback methods:</p>
+
+<dl>
+  <dt>{@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()}</dt>
+    <dd>The Backup Manager calls this method after you <a href="#RequestingBackup">request a
+backup</a>. In this method, you read your application data from the device and pass the data you
+want to back up to the Backup Manager, as described below in <a href="#PerformingBackup">Performing
+backup</a>.</dd>
+
+  <dt>{@link
+android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
+onRestore()}</dt>
+    <dd>The Backup Manager calls this method during a restore operation (you can <a
+href="#RequestingRestore">request a restore</a>, but the system automatically performs restore when
+the user re-installs your application). When it calls this method, the Backup Manager delivers your
+backup data, which you then restore to the device, as described below in <a
+href="#PerformingRestore">Performing restore</a>.</dd>
+</dl>
+
+
+
+<h3 id="PerformingBackup">Performing backup</h3>
+
+
+<p>When it's time to back up your application data, the Backup Manager calls your {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} method. This is where you must provide your application data to the Backup Manager so
+it can be saved to cloud storage.</p>
+
+<p>Only the Backup Manager can call your backup agent's {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} method. Each time that your application data changes and you want to perform a backup,
+you must request a backup operation by calling {@link
+android.app.backup.BackupManager#dataChanged()} (see <a href="#RequestingBackup">Requesting
+Backup</a> for more information). A backup request does not result in an immediate call to your
+{@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} method. Instead, the Backup Manager waits for an appropriate time, then performs
+backup for all applications that have requested a backup since the last backup was performed.</p>
+
+<p class="note"><strong>Tip:</strong> While developing your application, you can initiate an
+immediate backup operation from the Backup Manager with the <a
+href="{@docRoot}tools/help/bmgr.html">{@code bmgr} tool</a>.</p>
+
+<p>When the Backup Manager calls your {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} method, it passes three parameters:</p>
+
+<dl>
+  <dt>{@code oldState}</dt>
+    <dd>An open, read-only {@link android.os.ParcelFileDescriptor} pointing to the last backup
+state provided by your application. This is not the backup data from cloud storage, but a
+local representation of the data that was backed up the last time {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} was called (as defined by {@code newState}, below, or from {@link
+android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
+onRestore()}&mdash;more about this in the next section). Because {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} does not allow you to read existing backup data in
+the cloud storage, you can use this local representation to determine whether your data has changed
+since the last backup.</dd>
+  <dt>{@code data}</dt>
+    <dd>A {@link android.app.backup.BackupDataOutput} object, which you use to deliver your backup
+data to the Backup Manager.</dd>
+  <dt>{@code newState}</dt>
+    <dd>An open, read/write {@link android.os.ParcelFileDescriptor} pointing to a file in which
+you must write a representation of the data that you delivered to {@code data} (a representation
+can be as simple as the last-modified timestamp for your file). This object is
+returned as {@code oldState} the next time the Backup Manager calls your {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} method. If you do not write your backup data to {@code newState}, then {@code oldState}
+will point to an empty file next time Backup Manager calls {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()}.</dd>
+</dl>
+
+<p>Using these parameters, you should implement your {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} method to do the following:</p>
+
+<ol>
+  <li>Check whether your data has changed since the last backup by comparing {@code oldState} to
+your current data. How you read data in {@code oldState} depends on how you originally wrote it to
+{@code newState} (see step 3). The easiest way to record the state of a file is with its
+last-modified timestamp. For example, here's how you can read and compare a timestamp from {@code
+oldState}:
+    <pre>
+// Get the oldState input stream
+FileInputStream instream = new FileInputStream(oldState.getFileDescriptor());
+DataInputStream in = new DataInputStream(instream);
+
+try {
+    // Get the last modified timestamp from the state file and data file
+    long stateModified = in.readLong();
+    long fileModified = mDataFile.lastModified();
+
+    if (stateModified != fileModified) {
+        // The file has been modified, so do a backup
+        // Or the time on the device changed, so be safe and do a backup
+    } else {
+        // Don't back up because the file hasn't changed
+        return;
+    }
+} catch (IOException e) {
+    // Unable to read state file... be safe and do a backup
+}
+</pre>
+    <p>If nothing has changed and you don't need to back up, skip to step 3.</p>
+  </li>
+  <li>If your data has changed, compared to {@code oldState}, write the current data to
+{@code data} to back it up to the cloud storage.
+    <p>You must write each chunk of data as an "entity" in the {@link
+android.app.backup.BackupDataOutput}. An entity is a flattened binary data
+record that is identified by a unique key string. Thus, the data set that you back up is
+conceptually a set of key-value pairs.</p>
+    <p>To add an entity to your backup data set, you must:</p>
+    <ol>
+      <li>Call {@link android.app.backup.BackupDataOutput#writeEntityHeader(String,int)
+writeEntityHeader()}, passing a unique string key for the data you're about to write and the data
+size.</li>
+      <li>Call {@link android.app.backup.BackupDataOutput#writeEntityData(byte[],int)
+writeEntityData()}, passing a byte buffer that contains your data and the number of bytes to write
+from the buffer (which should match the size passed to {@link
+android.app.backup.BackupDataOutput#writeEntityHeader(String,int) writeEntityHeader()}).</li>
+    </ol>
+    <p>For example, the following code flattens some data into a byte stream and writes it into a
+single entity:</p>
+    <pre>
+// Create buffer stream and data output stream for our data
+ByteArrayOutputStream bufStream = new ByteArrayOutputStream();
+DataOutputStream outWriter = new DataOutputStream(bufStream);
+// Write structured data
+outWriter.writeUTF(mPlayerName);
+outWriter.writeInt(mPlayerScore);
+// Send the data to the Backup Manager via the BackupDataOutput
+byte[] buffer = bufStream.toByteArray();
+int len = buffer.length;
+data.writeEntityHeader(TOPSCORE_BACKUP_KEY, len);
+data.writeEntityData(buffer, len);
+</pre>
+    <p>Perform this for each piece of data that you want to back up. How you divide your data into
+entities is up to you (and you might use just one entity).</p>
+  </li>
+  <li>Whether or not you perform a backup (in step 2), write a representation of the current data to
+the {@code newState} {@link android.os.ParcelFileDescriptor}. The Backup Manager retains this object
+locally as a representation of the data that is currently backed up. It passes this back to you as
+{@code oldState} the next time it calls {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} so you can determine whether another backup is necessary (as handled in step 1). If you
+do not write the current data state to this file, then
+{@code oldState} will be empty during the next callback.
+    <p>The following example saves a representation of the current data into {@code newState} using
+the file's last-modified timestamp:</p>
+    <pre>
+FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor());
+DataOutputStream out = new DataOutputStream(outstream);
+
+long modified = mDataFile.lastModified();
+out.writeLong(modified);
+</pre>
+  </li>
+</ol>
+
+<p class="caution"><strong>Caution:</strong> If your application data is saved to a file, make sure
+that you use synchronized statements while accessing the file so that your backup agent does not
+read the file while an Activity in your application is also writing the file.</p>
+
+
+
+
+<h3 id="PerformingRestore">Performing restore</h3>
+
+<p>When it's time to restore your application data, the Backup Manager calls your backup
+agent's {@link android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
+onRestore()} method. When it calls this method, the Backup Manager delivers your backup data so
+you can restore it onto the device.</p>
+
+<p>Only the Backup Manager can call {@link
+android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
+onRestore()}, which happens automatically when the system installs your application and
+finds existing backup data. However, you can request a restore operation for
+your application by calling {@link
+android.app.backup.BackupManager#requestRestore(RestoreObserver) requestRestore()} (see <a
+href="#RequestingRestore">Requesting restore</a> for more information).</p>
+
+<p class="note"><strong>Note:</strong> While developing your application, you can also request a
+restore operation with the <a href="{@docRoot}tools/help/bmgr.html">{@code bmgr}
+tool</a>.</p>
+
+<p>When the Backup Manager calls your {@link
+android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
+onRestore()} method, it passes three parameters:</p>
+
+<dl>
+  <dt>{@code data}</dt>
+    <dd>A {@link android.app.backup.BackupDataInput}, which allows you to read your backup
+data.</dd>
+  <dt>{@code appVersionCode}</dt>
+    <dd>An integer representing the value of your application's <a
+href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code android:versionCode}</a>
+manifest attribute, as it was when this data was backed up. You can use this to cross-check the
+current application version and determine if the data format is compatible. For more
+information about using this to handle different versions of restore data, see the section
+below about <a href="#RestoreVersion">Checking the Restore Data Version</a>.</dd>
+  <dt>{@code newState}</dt>
+    <dd>An open, read/write {@link android.os.ParcelFileDescriptor} pointing to a file in which
+you must write the final backup state that was provided with {@code data}. This object is
+returned as {@code oldState} the next time {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} is called. Recall that you must also write the same {@code newState} object in the
+{@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} callback&mdash;also doing it here ensures that the {@code oldState} object given to
+{@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} is valid even the first time {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} is called after the device is restored.</dd>
+</dl>
+
+<p>In your implementation of {@link
+android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
+onRestore()}, you should call {@link android.app.backup.BackupDataInput#readNextHeader()} on the
+{@code data} to iterate
+through all entities in the data set. For each entity found, do the following:</p>
+
+<ol>
+  <li>Get the entity key with {@link android.app.backup.BackupDataInput#getKey()}.</li>
+  <li>Compare the entity key to a list of known key values that you should have declared as static
+final strings inside your {@link android.app.backup.BackupAgent} class. When the key matches one of
+your known key strings, enter into a statement to extract the entity data and save it to the device:
+    <ol>
+      <li>Get the entity data size with {@link
+android.app.backup.BackupDataInput#getDataSize()} and create a byte array of that size.</li>
+      <li>Call {@link android.app.backup.BackupDataInput#readEntityData(byte[],int,int)
+readEntityData()} and pass it the byte array, which is where the data will go, and specify the
+start offset and the size to read.</li>
+      <li>Your byte array is now full and you can read the data and write it to the device
+however you like.</li>
+    </ol>
+  </li>
+  <li>After you read and write your data back to the device, write the state of your data to the
+{@code newState} parameter the same as you do during {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()}.
+</ol>
+
+<p>For example, here's how you can restore the data backed up by the example in the previous
+section:</p>
+
+<pre>
+&#64;Override
+public void onRestore(BackupDataInput data, int appVersionCode,
+                      ParcelFileDescriptor newState) throws IOException {
+    // There should be only one entity, but the safest
+    // way to consume it is using a while loop
+    while (data.readNextHeader()) {
+        String key = data.getKey();
+        int dataSize = data.getDataSize();
+
+        // If the key is ours (for saving top score). Note this key was used when
+        // we wrote the backup entity header
+        if (TOPSCORE_BACKUP_KEY.equals(key)) {
+            // Create an input stream for the BackupDataInput
+            byte[] dataBuf = new byte[dataSize];
+            data.readEntityData(dataBuf, 0, dataSize);
+            ByteArrayInputStream baStream = new ByteArrayInputStream(dataBuf);
+            DataInputStream in = new DataInputStream(baStream);
+
+            // Read the player name and score from the backup data
+            mPlayerName = in.readUTF();
+            mPlayerScore = in.readInt();
+
+            // Record the score on the device (to a file or something)
+            recordScore(mPlayerName, mPlayerScore);
+        } else {
+            // We don't know this entity key. Skip it. (Shouldn't happen.)
+            data.skipEntityData();
+        }
+    }
+
+    // Finally, write to the state blob (newState) that describes the restored data
+    FileOutputStream outstream = new FileOutputStream(newState.getFileDescriptor());
+    DataOutputStream out = new DataOutputStream(outstream);
+    out.writeUTF(mPlayerName);
+    out.writeInt(mPlayerScore);
+}
+</pre>
+
+<p>In this example, the {@code appVersionCode} parameter passed to {@link
+android.app.backup.BackupAgent#onRestore onRestore()} is not used. However, you might want to use
+it if you've chosen to perform backup when the user's version of the application has actually moved
+backward (for example, the user went from version 1.5 of your app to 1.0). For more information, see
+the section about <a href="#RestoreVersion">Checking the Restore Data Version</a>.</p>
+
+<div class="special">
+<p>For an example implementation of {@link android.app.backup.BackupAgent}, see the <a
+href="{@docRoot}resources/samples/BackupRestore/src/com/example/android/backuprestore/ExampleAgent.html">{@code
+ExampleAgent}</a> class in the <a
+href="{@docRoot}resources/samples/BackupRestore/index.html">Backup and Restore</a> sample
+application.</p>
+</div>
+
+
+
+
+
+
+<h2 id="BackupAgentHelper">Extending BackupAgentHelper</h2>
+
+<p>You should build your backup agent using {@link android.app.backup.BackupAgentHelper} if you want
+to back up complete files (from either {@link android.content.SharedPreferences} or <a
+href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>).
+Building your backup agent with {@link android.app.backup.BackupAgentHelper} requires far less
+code than extending {@link android.app.backup.BackupAgent}, because you don't have to implement
+{@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} and {@link
+android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
+onRestore()}.</p>
+
+<p>Your implementation of {@link android.app.backup.BackupAgentHelper} must
+use one or more backup helpers. A backup helper is a specialized
+component that {@link android.app.backup.BackupAgentHelper} summons to perform backup and
+restore operations for a particular type of data. The Android framework currently provides two
+different helpers:</p>
+<ul>
+  <li>{@link android.app.backup.SharedPreferencesBackupHelper} to backup {@link
+android.content.SharedPreferences} files.</li>
+  <li>{@link android.app.backup.FileBackupHelper} to backup files from <a
+href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>.</li>
+</ul>
+
+<p>You can include multiple helpers in your {@link android.app.backup.BackupAgentHelper}, but only
+one helper is needed for each data type. That is, if you have multiple {@link
+android.content.SharedPreferences} files, then you need only one {@link
+android.app.backup.SharedPreferencesBackupHelper}.</p>
+
+<p>For each helper you want to add to your {@link android.app.backup.BackupAgentHelper}, you must do
+the following during your {@link android.app.backup.BackupAgent#onCreate()} method:</p>
+<ol>
+  <li>Instantiate in instance of the desired helper class. In the class constructor, you must
+specify the appropriate file(s) you want to backup.</li>
+  <li>Call {@link android.app.backup.BackupAgentHelper#addHelper(String,BackupHelper) addHelper()}
+to add the helper to your {@link android.app.backup.BackupAgentHelper}.</li>
+</ol>
+
+<p>The following sections describe how to create a backup agent using each of the available
+helpers.</p>
+
+
+
+<h3 id="SharedPreferences">Backing up SharedPreferences</h3>
+
+<p>When you instantiate a {@link android.app.backup.SharedPreferencesBackupHelper}, you must
+include the name of one or more {@link android.content.SharedPreferences} files.</p>
+
+<p>For example, to back up a {@link android.content.SharedPreferences} file named
+"user_preferences", a complete backup agent using {@link android.app.backup.BackupAgentHelper} looks
+like this:</p>
+
+<pre>
+public class MyPrefsBackupAgent extends BackupAgentHelper {
+    // The name of the SharedPreferences file
+    static final String PREFS = "user_preferences";
+
+    // A key to uniquely identify the set of backup data
+    static final String PREFS_BACKUP_KEY = "prefs";
+
+    // Allocate a helper and add it to the backup agent
+    &#64;Override
+    public void onCreate() {
+        SharedPreferencesBackupHelper helper =
+                new SharedPreferencesBackupHelper(this, PREFS);
+        addHelper(PREFS_BACKUP_KEY, helper);
+    }
+}
+</pre>
+
+<p>That's it! That's your entire backup agent. The {@link
+android.app.backup.SharedPreferencesBackupHelper} includes all the code
+needed to backup and restore a {@link android.content.SharedPreferences} file.</p>
+
+<p>When the Backup Manager calls {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} and {@link
+android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
+onRestore()}, {@link android.app.backup.BackupAgentHelper} calls your backup helpers to perform
+backup and restore for your specified files.</p>
+
+<p class="note"><strong>Note:</strong> The methods of {@link android.content.SharedPreferences}
+are threadsafe, so
+you can safely read and write the shared preferences file from your backup agent and
+other activities.</p>
+
+
+
+<h3 id="Files">Backing up other files</h3>
+
+<p>When you instantiate a {@link android.app.backup.FileBackupHelper}, you must include the name of
+one or more files that are saved to your application's <a
+href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>
+(as specified by {@link android.content.ContextWrapper#getFilesDir()}, which is the same
+location where {@link android.content.Context#openFileOutput(String,int) openFileOutput()} writes
+files).</p>
+
+<p>For example, to backup two files named "scores" and "stats," a backup agent using {@link
+android.app.backup.BackupAgentHelper} looks like this:</p>
+
+<pre>
+public class MyFileBackupAgent extends BackupAgentHelper {
+    // The name of the file
+    static final String TOP_SCORES = "scores";
+    static final String PLAYER_STATS = "stats";
+
+    // A key to uniquely identify the set of backup data
+    static final String FILES_BACKUP_KEY = "myfiles";
+
+    // Allocate a helper and add it to the backup agent
+    &#64;Override
+    public void onCreate() {
+        FileBackupHelper helper = new FileBackupHelper(this,
+                TOP_SCORES, PLAYER_STATS);
+        addHelper(FILES_BACKUP_KEY, helper);
+    }
+}
+</pre>
+
+<p>The {@link android.app.backup.FileBackupHelper} includes all the code necessary to backup and
+restore files that are saved to your application's <a
+href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal storage</a>..</p>
+
+<p>However, reading and writing to files on internal storage is <strong>not threadsafe</strong>. To
+ensure that your backup agent does not read or write your files at the same time as your activities,
+you must use synchronized statements each time you perform a read or write. For example,
+in any Activity where you read and write the file, you need an object to use as the intrinsic
+lock for the synchronized statements:</p>
+
+<pre>
+// Object for intrinsic lock
+static final Object sDataLock = new Object();
+</pre>
+
+<p>Then create a synchronized statement with this lock each time you read or write the files. For
+example, here's a synchronized statement for writing the latest score in a game to a file:</p>
+
+<pre>
+try {
+    synchronized (MyActivity.sDataLock) {
+        File dataFile = new File({@link android.content.Context#getFilesDir()}, TOP_SCORES);
+        RandomAccessFile raFile = new RandomAccessFile(dataFile, "rw");
+        raFile.writeInt(score);
+    }
+} catch (IOException e) {
+    Log.e(TAG, "Unable to write to file");
+}
+</pre>
+
+<p>You should synchronize your read statements with the same lock.</p>
+
+<p>Then, in your {@link android.app.backup.BackupAgentHelper}, you must override {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} and {@link
+android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
+onRestore()} to synchronize the backup and restore operations with the same
+intrinsic lock. For example, the {@code MyFileBackupAgent} example from above needs the following
+methods:</p>
+
+<pre>
+&#64;Override
+public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+          ParcelFileDescriptor newState) throws IOException {
+    // Hold the lock while the FileBackupHelper performs backup
+    synchronized (MyActivity.sDataLock) {
+        super.onBackup(oldState, data, newState);
+    }
+}
+
+&#64;Override
+public void onRestore(BackupDataInput data, int appVersionCode,
+        ParcelFileDescriptor newState) throws IOException {
+    // Hold the lock while the FileBackupHelper restores the file
+    synchronized (MyActivity.sDataLock) {
+        super.onRestore(data, appVersionCode, newState);
+    }
+}
+</pre>
+
+<p>That's it. All you need to do is add your {@link android.app.backup.FileBackupHelper} in the
+{@link android.app.backup.BackupAgent#onCreate()} method and override {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} and {@link
+android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor)
+onRestore()} to synchronize read and write operations.</p>
+
+<div class="special">
+<p>For an example implementation of {@link
+android.app.backup.BackupAgentHelper} with {@link android.app.backup.FileBackupHelper}, see the
+{@code FileHelperExampleAgent} class in the <a
+href="{@docRoot}resources/samples/BackupRestore/index.html">Backup and Restore</a> sample
+application.</p>
+</div>
+
+
+
+
+
+
+<h2 id="RestoreVersion">Checking the restore data version</h2>
+
+<p>When the Backup Manager saves your data to cloud storage, it automatically includes the version
+of your application, as defined by your manifest file's <a
+href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code android:versionCode}</a>
+attribute. Before the Backup Manager calls your backup agent to restore your data, it
+looks at the <a
+href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code
+android:versionCode}</a> of the installed application and compares it to the value
+recorded in the restore data set. If the version recorded in the restore data set is
+<em>newer</em> than the application version on the device, then the user has downgraded their
+application. In this case, the Backup Manager will abort the restore operation for your application
+and not call your {@link
+android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}
+method, because the restore set is considered meaningless to an older version.</p>
+
+<p>You can override this behavior with the <a
+href="{@docRoot}guide/topics/manifest/application-element.html#restoreany">{@code
+android:restoreAnyVersion}</a> attribute. This attribute is either "{@code true}" or "{@code
+false}" to indicate whether you want to restore the application regardless of the restore set
+version. The default value is "{@code false}". If you define this to be "{@code true}" then the
+Backup Manager will ignore the <a
+href="{@docRoot}guide/topics/manifest/manifest-element.html#vcode">{@code android:versionCode}</a>
+and call your {@link
+android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}
+method in all cases. In doing so, you can manually check for the version difference in your {@link
+android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}
+method and take any steps necessary to make the data compatible if the versions conflict.</p>
+
+<p>To help you handle different versions during a restore operation, the {@link
+android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}
+method passes you the version code included with the restore data set as the {@code appVersionCode}
+parameter. You can then query the current application's version code with the {@link
+android.content.pm.PackageInfo#versionCode PackageInfo.versionCode} field. For example:</p>
+
+<pre>
+PackageInfo info;
+try {
+    String name = {@link android.content.ContextWrapper#getPackageName() getPackageName}();
+    info = {@link android.content.ContextWrapper#getPackageManager
+getPackageManager}().{@link android.content.pm.PackageManager#getPackageInfo(String,int)
+getPackageInfo}(name,0);
+} catch (NameNotFoundException nnfe) {
+    info = null;
+}
+
+int version;
+if (info != null) {
+    version = info.versionCode;
+}
+</pre>
+
+<p>Then simply compare the {@code version} acquired from {@link android.content.pm.PackageInfo}
+to the {@code appVersionCode} passed into {@link
+android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}.
+</p>
+
+<p class="caution"><strong>Caution:</strong> Be certain you understand the consequences of setting
+<a href="{@docRoot}guide/topics/manifest/application-element.html#restoreany">{@code
+android:restoreAnyVersion}</a> to "{@code true}" for your application. If each version of your
+application that supports backup does not properly account for variations in your data format during
+{@link
+android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()},
+then the data on the device could be saved in a format incompatible with the version currently
+installed on the device.</p>
+
+
+
+<h2 id="RequestingBackup">Requesting backup</h2>
+
+<p>You can request a backup operation at any time by calling {@link
+android.app.backup.BackupManager#dataChanged()}. This method notifies the Backup Manager that you'd
+like to backup your data using your backup agent. The Backup Manager then calls your backup
+agent's {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()} method at an opportune time in the future. Typically, you should
+request a backup each time your data changes (such as when the user changes an application
+preference that you'd like to back up). If you call {@link
+android.app.backup.BackupManager#dataChanged()} several times consecutively, before the Backup
+Manager requests a backup from your agent, your agent still receives just one call to {@link
+android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor,BackupDataOutput,ParcelFileDescriptor)
+onBackup()}.</p>
+
+<p class="note"><strong>Note:</strong> While developing your application, you can request a
+backup and initiate an immediate backup operation with the <a
+href="{@docRoot}tools/help/bmgr.html">{@code bmgr}
+tool</a>.</p>
+
+
+<h2 id="RequestingRestore">Requesting restore</h2>
+
+<p>During the normal life of your application, you shouldn't need to request a restore operation.
+They system automatically checks for backup data and performs a restore when your application is
+installed. However, you can manually request a restore operation by calling {@link
+android.app.backup.BackupManager#requestRestore(RestoreObserver) requestRestore()}, if necessary. In
+which case, the Backup Manager calls your {@link
+android.app.backup.BackupAgent#onRestore(BackupDataInput,int,ParcelFileDescriptor) onRestore()}
+implementation, passing the data from the current set of backup data.</p>
+
+<p class="note"><strong>Note:</strong> While developing your application, you can request a
+restore operation with the <a href="{@docRoot}tools/help/bmgr.html">{@code bmgr}
+tool</a>.</p>
+
+
+<h2 id="Migrating">Migrating to Auto Backup</h2>
+<p>You can transition your app to full-data backups by setting <a href="{@docRoot}reference/android/R.attr.html#fullBackupOnly">android:fullBackupOnly</a> to <code>true</code> in the <code>&lt;application></code> element in the manifest file. When
+running on a device with Android 5.1 (API level 22) or lower, your app ignores
+this value in the manifest, and continues performing Key/Value Backups. When
+running on a device with Android 6.0 (API level 23) or higher, your app performs
+Auto Backup instead of Key/Value Backup.
\ No newline at end of file
diff --git a/docs/html/guide/topics/data/testingbackup.jd b/docs/html/guide/topics/data/testingbackup.jd
new file mode 100644
index 0000000..6ff5837
--- /dev/null
+++ b/docs/html/guide/topics/data/testingbackup.jd
@@ -0,0 +1,181 @@
+page.title=Testing Backup and Restore
+page.tags=backup, marshmallow, androidm
+page.keywords=backup, restore, testing
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#HowBackupWorks">How backup works</a></li>
+    <li><a href="#Prerequisites">Prerequisites</a></li>
+    <li><a href="#Preparing">Preparing your device or emulator</a></li>
+    <li><a href="#TestingBackup">Testing backup</a></li>
+    <li><a href="#TestingRestore">Testing restore</a></li>
+    <li><a href="#Troubleshooting">Troubleshooting</a></li>
+  </ol>
+
+</div>
+</div>
+
+<p>This page shows you how to manually trigger Auto Backup, Key/Value Backup, and restore operations to
+ensure your app saves and restores data properly.
+
+<h2 id="HowBackupWorkso">How backup works</h2>
+<p>The section describes various pieces in the Android backup framework and how they
+interact with apps that support Auto Backup and Key/Value Backup. During the app
+development phase, most of the inner working of the framework were abstracted away, so you didn't need to know this information. However, during the
+testing phase, an understanding of these concepts is important.
+
+<p>The following diagram illustrates how data flows during backup and restore:
+
+<img src="images/backup-framework.png" alt="backup-framework">
+
+<p>The <em>Backup Manager Service</em> is an Android system
+service which orchestrates and initiates backup and restore operations. The service
+is accessible through the {@link android.app.backup.BackupManager}
+API. During a backup operation, the service queries your app for backup data,
+then hands it to the <em>backup transport</em>, which then archives the data.
+During a restore operation, the backup manager service retrieves the backup data
+from the backup transport and restores the data to the device.
+
+<p><em>Backup Transports</em> are Android components that are responsible
+for storing and retrieving backups. An Android device can have zero or more
+backup transports, but only one of those transports can be marked active. The
+available backup transports may differ from device to device (due to
+customizations by device manufacturers and service providers), but most Google
+Play enabled devices ship with the following transports:
+</p><ul>
+<li><strong>Google GMS Transport</strong>(default) - the active backup
+transport on most devices, part of <a href="https://www.android.com/gms/">Google Mobile Services</a>. This documentation assumes that users are using the
+Google GMS transport. This transport stores Auto Backup data in a private folder in the
+user's Google Drive account. Key/Value Backup data is stored in the <a href="{@docRoot}google/backup/index.html">Android Backup Service</a>.
+<li><strong>Local Transport</strong> - stores backup data locally on the device.
+This transport is typically used for development/debugging purposes and is not
+useful in the real world.</li></ul>
+
+<p>If a device does not have any backup transports, then the data cannot be
+backed up. Your app is not adversely affected.
+
+<p class="caution"><strong>Caution:</strong> Because the backup transport
+can differ from device to device, Android cannot guarantee the security
+of your data while using backup. Be cautious about using backup to store
+sensitive data, such as usernames and passwords.
+
+<h2 id="Prerequisites">Prerequisites</h2>
+<p>You need to know a bit about the following tools:
+
+<ul>
+  <li><a href="{@docRoot}studio/command-line/adb.html">adb</a>
+- to run commands on the device or emulator
+  <li><a href="{@docRoot}studio/command-line/bmgr.html">bmgr</a> - to
+perform various backup and restore operations
+  <li><a href="{@docRoot}studio/command-line/logcat.html">logcat</a>
+- to see the output of backup and restore operations.</li></ul>
+
+<h2 id="Preparing">Preparing your device or
+emulator</h2>
+<p>Prepare your device or emulator for backup testing by working through the
+following checklist:
+<ul>
+  <li>For Auto Backup, check that you are using a device or emulator running
+Android 6.0 (API level 23) or higher.</li>
+  <li>For Key/Value Backup, check that you are using a device or emulator running
+Android 2.2 (API level 8) or higher.</li>
+  <li>Check that backup and restore is enabled on the device or emulator. There
+are two ways to check: <ul>
+    <li>On the device, go to <strong>Settings -> Backup &amp; Restore</strong>.
+    <li>From adb shell, run <code>bmgr enable</code></li></ul>
+    <p>On physical devices, backup and restore is typically enabled during the
+initial setup wizard. Emulators do not run the setup wizard, so don't forget to
+enable backup and specify a backup account in device settings.
+  <li>Make sure the GMS Backup Transport is available and active by running the
+command from adb shell:
+  <pre class="prettyprint">$ bmgr list transports</pre>
+  <p>Then, check the console for the following output:
+  <pre class="prettyprint">android/com.android.internal.backup.LocalTransport
+* com.google.android.gms/.backup.BackupTransportService</pre>
+  <p>Physical devices without Google Play and emulators without Google APIs
+might not include the GMS Backup Transport. This article assumes you are using
+the GMS Backup Transport. You can test backup and restore with other backup
+transports, but the procedure and output can differ.
+</ul>
+
+<h2 id="TestingBackup">Testing backup</h2>
+<p>To initiate a backup of your app, run the following command:
+
+<pre class="prettyprint">$ bmgr backupnow &lt;PACKAGE></pre>
+
+<p>The <code>backupnow</code> command runs either a Key/Value Backup or Auto Backup depending on
+the package's manifest declarations. Check logcat to see the output of the
+backup procedure. For example:
+
+<pre class="prettyprint">D/BackupManagerService: fullTransportBackup()
+I/GmsBackupTransport: Attempt to do full backup on &lt;PACKAGE>
+
+---- or ----
+
+V/BackupManagerService: Scheduling immediate backup pass
+D/PerformBackupTask: starting key/value Backup of BackupRequest{pkg=&lt;PACKAGE>}
+</pre>
+
+<p>If the <code>backupnow</code> command is not available on your device, you need to run one
+of the following commands:
+<ul>
+  <li>For Auto Backups, run: <code>bmgr fullbackup &lt;PACKAGE></code>
+  <li>For Key/Value Backups, schedule and run your backup with the following
+commands:
+  <pre class="prettyprint">$ bmgr backup &lt;PACKAGE>
+$ bmgr run</pre>
+  <p><code>bmgr backup</code> adds your app to the Backup Manager's queue. <code>bmgr run</code> initiates the
+backup operation, which forces the Backup Manager to perform all backup requests
+that are in its queue.
+  </li></ul>
+
+<h2 id="TestingRestore">Testing restore</h2>
+<p>To manually initiate a restore, run the following command:
+
+<pre class="prettyprint">$ bmgr restore &lt;PACKAGE></pre>
+
+<p>Warning: This action stops your app and wipes its data before performing the
+restore operation.
+
+<p>Then, check logcat to see the output of the restore procedure. For example:
+
+<pre class="prettyprint">V/BackupManagerService: beginRestoreSession: pkg=&lt;PACKAGE> transport=null
+V/RestoreSession: restorePackage pkg=&lt;PACKAGE> token=368abb4465c5c683
+...
+I/BackupManagerService: Restore complete.
+</pre>
+
+<p>You also can test automatic restore for your app by uninstalling and
+reinstalling your app either with <code>adb</code> or through the Google
+Play Store app.
+
+<h2 id="Troubleshooting">Troubleshooting</h2>
+<p><strong>Exceeded Quota</strong>
+
+<p>If you see the the following messages in logcat:
+
+<pre class="prettyprint">I/PFTBT: Transport rejected backup of &lt;PACKAGE>, skipping
+
+--- or ---
+
+I/PFTBT: Transport quota exceeded for package: &lt;PACKAGE>
+</pre>
+
+<p>Your app has exceeded the quota and has been banned from backing up
+data on this device. To lift the ban, either factory reset your device or change
+the backup account.
+
+<p><strong>Full Backup Not Possible</strong>
+
+<p>If you see the the following message in logcat:
+
+<pre class="prettyprint">I/BackupManagerService: Full backup not currently possible -- key/value backup not yet run?
+</pre>
+
+<p>The fullbackup operation failed because no Key/Value Backup operation has yet
+occurred on the device. Trigger a Key/Value Backup with the command <code>bmgr
+run </code>and then try again.
\ No newline at end of file
diff --git a/docs/html/guide/topics/resources/drawable-resource.jd b/docs/html/guide/topics/resources/drawable-resource.jd
index aae0cba..4587ae4 100644
--- a/docs/html/guide/topics/resources/drawable-resource.jd
+++ b/docs/html/guide/topics/resources/drawable-resource.jd
@@ -1270,7 +1270,7 @@
 progressively reveal the image:</p>
 <pre>
 ImageView imageview = (ImageView) findViewById(R.id.image);
-ClipDrawable drawable = (ClipDrawable) imageview.getDrawable();
+ClipDrawable drawable = (ClipDrawable) imageview.getBackground();
 drawable.setLevel(drawable.getLevel() + 1000);
 </pre>
 
diff --git a/docs/html/guide/topics/ui/accessibility/services.jd b/docs/html/guide/topics/ui/accessibility/services.jd
index c6db855..dbc69ef 100644
--- a/docs/html/guide/topics/ui/accessibility/services.jd
+++ b/docs/html/guide/topics/ui/accessibility/services.jd
@@ -79,22 +79,15 @@
 as shown in the following sample:</p>
 
 <pre>
-&lt;manifest&gt;
-  ...
-  &lt;uses-permission ... /&gt;
-  ...
   &lt;application&gt;
-    ...
     &lt;service android:name=&quot;.MyAccessibilityService&quot;
-        android:label=&quot;@string/accessibility_service_label&quot;
-        android:permission=&quot;android.permission.BIND_ACCESSIBILITY_SERVICE&quot&gt;
+        android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
+        android:label=&quot;@string/accessibility_service_label&quot;&gt;
       &lt;intent-filter&gt;
         &lt;action android:name=&quot;android.accessibilityservice.AccessibilityService&quot; /&gt;
       &lt;/intent-filter&gt;
     &lt;/service&gt;
-    &lt;uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" /&gt;
   &lt;/application&gt;
-&lt;/manifest&gt;
 </pre>
 
 <p>These declarations are required for all accessibility services deployed on Android 1.6 (API Level
diff --git a/docs/html/guide/topics/ui/drag-drop.jd b/docs/html/guide/topics/ui/drag-drop.jd
index 8e4297f..d6e07a56 100644
--- a/docs/html/guide/topics/ui/drag-drop.jd
+++ b/docs/html/guide/topics/ui/drag-drop.jd
@@ -408,8 +408,8 @@
         <td>{@link android.view.DragEvent#ACTION_DRAG_ENTERED}</td>
         <td style="text-align: center;">X</td>
         <td style="text-align: center;">X</td>
-        <td style="text-align: center;">X</td>
-        <td style="text-align: center;">X</td>
+        <td style="text-align: center;">&nbsp;</td>
+        <td style="text-align: center;">&nbsp;</td>
         <td style="text-align: center;">&nbsp;</td>
         <td style="text-align: center;">&nbsp;</td>
     </tr>
@@ -442,7 +442,7 @@
     </tr>
     <tr>
         <td>{@link android.view.DragEvent#ACTION_DRAG_ENDED}</td>
-        <td style="text-align: center;">X</td>
+        <td style="text-align: center;">&nbsp;</td>
         <td style="text-align: center;">X</td>
         <td style="text-align: center;">&nbsp;</td>
         <td style="text-align: center;">&nbsp;</td>
diff --git a/docs/html/topic/performance/_book.yaml b/docs/html/topic/performance/_book.yaml
index e053a2c..4021e85 100644
--- a/docs/html/topic/performance/_book.yaml
+++ b/docs/html/topic/performance/_book.yaml
@@ -1,34 +1,61 @@
 toc:
-- title: Reducing Network Battery Drain
-  path: /topic/performance/power/network/index.html
+- title: Optimizing for Battery Life
+  path: /topic/performance/power/index.html
   path_attributes:
   - name: description
-    value: Access the network while going easy on battery life.
+    value: Learn to make your app more battery-friendly.
   section:
-  - title: Collecting Network Traffic Data
-    path: /topic/performance/power/network/gather-data.html
-  - title: Analyzing Network Traffic Data
-    path: /topic/performance/power/network/analyze-data.html
-  - title: Optimizing User-Initiated Network Use
-    path: /topic/performance/power/network/action-user-traffic.html
-  - title: Optimizing Server-Initiated Network Use
-    path: /topic/performance/power/network/action-server-traffic.html
-  - title: Optimizing General Network Use
-    path: /topic/performance/power/network/action-any-traffic.html
-- title: Implementing Doze
-  path: /training/monitoring-device-state/doze-standby.html
+  - title: Network Use and Battery Consumption
+    path: /topic/performance/power/network/index.html
+    section:
+    - title: Collecting Network Traffic Data
+      path: /topic/performance/power/network/gather-data.html
+    - title: Analyzing Data Traffic
+      path: /topic/performance/power/network/analyze-data.html
+    - title: Optimizing User-Initiated Network Use
+      path: /topic/performance/power/network/action-user-traffic.html
+    - title: Optimizing App-Initiated Network Use
+      path: topic/performance/power/network/action-app-traffic.html
+    - title: Optimizing Server-Initiated Network Use
+      path: /topic/performance/power/network/action-server-traffic.html
+    - title: Optimizing General Network Use
+      path: /topic/performance/power/network/action-any-traffic.html
+  - title: Doze and App Standby
+    path: /training/monitoring-device-state/doze-standby.html
+    path_attributes:
+    - name: description
+      value: Help ensure the device isn't depleting the battery when not in use.
+  - title: Battery Historian
+    path: /topic/performance/power/battery-historian.html
+- title: Rendering
+  path: /topic/performance/rendering/index.html
   path_attributes:
   - name: description
-    value: Help ensure the device isn't depleting the battery when not in use.
+    value: Speed up your app's rendering
+  section:
+  - title: Reducing Overdraw
+    path: /topic/performance/rendering/overdraw.html
+  - title: Performance and View Hierarchies
+    path: /topic/performance/rendering/optimizing-view-hierarchies.html
+  - title: Analyzing with Profile GPU Rendering
+    path: /topic/performance/rendering/profile-gpu.html
+- title: Intelligent Job-Scheduling
+  path: /topic/performance/scheduling.html
+- title: Background Optimization
+  path: /topic/performance/background-optimization.html
+- title: Reducing APK Size
+  path: /topic/performance/reduce-apk-size.html
+- title: Reducing Image Download Sizes
+  path: /topic/performance/network-xfer.html
 - title: Launch-Time Performance
   path: /topic/performance/launch-time.html
 - title: Better Performance through Threading
   path: /topic/performance/threads.html
-- title: Optimizing View Hierarchies
-  path: /topic/performance/optimizing-view-hierarchies.html
-- title: Background Optimization
-  path: /topic/performance/background-optimization.html
-- title: Intelligent Job-Scheduling
-  path: /topic/performance/scheduling.html
-- title: Reducing APK Size
-  path: /topic/performance/reduce-apk-size.html
+- title: Manage Your App's Memory
+  path: /topic/performance/memory.html
+- title: Overview of Memory Managemement
+  path: /topic/performance/memory-overview.html
+  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.
+
diff --git a/docs/html/topic/performance/images/app-rankings.png b/docs/html/topic/performance/images/app-rankings.png
new file mode 100644
index 0000000..9dd60e5
--- /dev/null
+++ b/docs/html/topic/performance/images/app-rankings.png
Binary files differ
diff --git a/docs/html/topic/performance/images/bars.png b/docs/html/topic/performance/images/bars.png
new file mode 100644
index 0000000..3afea46
--- /dev/null
+++ b/docs/html/topic/performance/images/bars.png
Binary files differ
diff --git a/docs/html/topic/performance/images/beforeafterindexed.png b/docs/html/topic/performance/images/beforeafterindexed.png
new file mode 100644
index 0000000..dc7762e
--- /dev/null
+++ b/docs/html/topic/performance/images/beforeafterindexed.png
Binary files differ
diff --git a/docs/html/topic/performance/images/comparison.png b/docs/html/topic/performance/images/comparison.png
new file mode 100644
index 0000000..18f204c
--- /dev/null
+++ b/docs/html/topic/performance/images/comparison.png
Binary files differ
diff --git a/docs/html/topic/performance/images/decisions.png b/docs/html/topic/performance/images/decisions.png
new file mode 100644
index 0000000..d4f21f8
--- /dev/null
+++ b/docs/html/topic/performance/images/decisions.png
Binary files differ
diff --git a/docs/html/topic/performance/images/dropdown.png b/docs/html/topic/performance/images/dropdown.png
new file mode 100644
index 0000000..59ec6110
--- /dev/null
+++ b/docs/html/topic/performance/images/dropdown.png
Binary files differ
diff --git a/docs/html/topic/performance/images/generic-timeline.png b/docs/html/topic/performance/images/generic-timeline.png
new file mode 100644
index 0000000..04388b6
--- /dev/null
+++ b/docs/html/topic/performance/images/generic-timeline.png
Binary files differ
diff --git a/docs/html/topic/performance/images/moarparrots.png b/docs/html/topic/performance/images/moarparrots.png
new file mode 100644
index 0000000..ee10ec1
--- /dev/null
+++ b/docs/html/topic/performance/images/moarparrots.png
Binary files differ
diff --git a/docs/html/topic/performance/images/palette.png b/docs/html/topic/performance/images/palette.png
new file mode 100644
index 0000000..eb6be6b
--- /dev/null
+++ b/docs/html/topic/performance/images/palette.png
Binary files differ
diff --git a/docs/html/topic/performance/images/parrot.png b/docs/html/topic/performance/images/parrot.png
new file mode 100644
index 0000000..7a8b86a
--- /dev/null
+++ b/docs/html/topic/performance/images/parrot.png
Binary files differ
diff --git a/docs/html/topic/performance/images/pug-visualization.png b/docs/html/topic/performance/images/pug-visualization.png
new file mode 100644
index 0000000..8270d0e
--- /dev/null
+++ b/docs/html/topic/performance/images/pug-visualization.png
Binary files differ
diff --git a/docs/html/topic/performance/images/pugspecificdata.png b/docs/html/topic/performance/images/pugspecificdata.png
new file mode 100644
index 0000000..1c2be83
--- /dev/null
+++ b/docs/html/topic/performance/images/pugspecificdata.png
Binary files differ
diff --git a/docs/html/topic/performance/images/s-generic-closeup.png b/docs/html/topic/performance/images/s-generic-closeup.png
new file mode 100644
index 0000000..6685d51
--- /dev/null
+++ b/docs/html/topic/performance/images/s-generic-closeup.png
Binary files differ
diff --git a/docs/html/topic/performance/images/s-profiler-legend.png b/docs/html/topic/performance/images/s-profiler-legend.png
new file mode 100644
index 0000000..968fd38
--- /dev/null
+++ b/docs/html/topic/performance/images/s-profiler-legend.png
Binary files differ
diff --git a/docs/html/topic/performance/images/vq.gif b/docs/html/topic/performance/images/vq.gif
new file mode 100644
index 0000000..cbf6a35
--- /dev/null
+++ b/docs/html/topic/performance/images/vq.gif
Binary files differ
diff --git a/docs/html/topic/performance/index.jd b/docs/html/topic/performance/index.jd
index e08db15..2b6b197 100644
--- a/docs/html/topic/performance/index.jd
+++ b/docs/html/topic/performance/index.jd
@@ -1,4 +1,4 @@
-page.title=Performance
+page.title=Performance and Power
 page.article=true
 page.metaDescription=Improve your app's performance by learning how to optimize power consumption, launch times, and other important areas of performance.
 
diff --git a/docs/html/topic/performance/memory-overview.jd b/docs/html/topic/performance/memory-overview.jd
new file mode 100644
index 0000000..58067d2
--- /dev/null
+++ b/docs/html/topic/performance/memory-overview.jd
@@ -0,0 +1,288 @@
+page.title=Overview of Android Memory Management
+page.tags=ram,memory,paging,mmap
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+<h2>In this document</h2>
+<ol class="nolist">
+  <li><a href="#gc">Garbage collection</a></li>
+  <li><a href="#SharingRAM">Sharing Memory</a></li>
+  <li><a href="#AllocatingRAM">Allocating and Reclaiming App Memory</a></li>
+  <li><a href="#RestrictingMemory">Restricting App Memory</a></li>
+  <li><a href="#SwitchingApps">Switching Apps</a></li>
+</ol>
+<h2>See Also</h2>
+<ul>
+  <li><a href="{@docRoot}training/articles/memory.html">Manage Your App's Memory</a>
+  </li>
+  <li><a href="{@docRoot}studio/profile/investigate-ram.html">Investigating Your RAM Usage</a>
+  </li>
+</ul>
+
+</div>
+</div>
+
+<p>
+  The Android Runtime (ART) and Dalvik virtual machine use
+  <a href="http://en.wikipedia.org/wiki/Paging" class="external-link">paging</a>
+  and <a href="http://en.wikipedia.org/wiki/Memory-mapped_files" class="external-link">memory-mapping</a>
+  (mmapping) to manage memory. This means that any memory an app
+  modifies&mdash;whether by allocating
+  new objects or touching mmapped pages&mdash;remains resident in RAM and
+  cannot be paged out. The only way to release memory from an app is to release
+  object references that the app holds, making the memory available to the
+  garbage collector.
+  That is with one exception: any files
+  mmapped in without modification, such as code,
+  can be paged out of RAM if the system wants to use that memory elsewhere.
+</p>
+
+<p>
+  This page explains how Android manages app processes and memory
+  allocation. For more information about how to manage memory more efficiently
+  in your app, see
+  <a href="{@docRoot}training/articles/memory.html">Manage Your App's Memory</a>.
+</p>
+
+<!-- Section 1 #################################################### -->
+
+<h2 id="gc">Garbage collection</h2>
+
+<p>
+  A managed memory environment, like the ART or Dalvik virtual machine,
+  keeps track of each memory allocation. Once it determines
+  that a piece of memory is no longer being used by the program,
+  it frees it back to the heap, without any intervention from the programmer.
+  The mechanism for reclaiming unused memory
+  within a managed memory environment
+  is known as <i>garbage collection</i>. Garbage collection has two goals:
+  find data objects in a program that cannot be accessed in the future; and
+  reclaim the resources used by those objects.
+</p>
+
+<p>
+  Android’s memory heap is a generational one, meaning that there are
+  different buckets of allocations that it tracks,
+  based on the expected life and size of an object being allocated.
+  For example, recently allocated objects belong in the <i>Young generation</i>.
+  When an object stays active long enough, it can be promoted
+  to an older generation, followed by a permanent generation.
+</p>
+
+<p>
+  Each heap generation has its own dedicated upper limit on the amount
+  of memory that objects there can occupy. Any time a generation starts
+  to fill up, the system executes a garbage collection
+  event in an attempt to free up memory. The duration of the garbage collection
+  depends on which generation of objects it's collecting
+  and how many active objects are in each generation.
+</p>
+
+<p>
+  Even though garbage collection can be quite fast, it can still
+  affect your app's performance. You don’t generally control
+  when a garbage collection event occurs from within your code.
+  The system has a running set of criteria for determining when to perform
+  garbage collection. When the criteria are satisfied,
+  the system stops executing the process and begins garbage collection. If
+  garbage collection occurs in the middle of an intensive processing loop
+  like an animation or during music playback, it can increase processing time.
+  This increase can potentially push code execution in your app past the
+  recommended 16ms threshold for efficient and smooth frame rendering.
+</p>
+
+<p>
+  Additionally, your code flow may perform kinds of work that
+  force garbage collection events to occur
+  more often or make them last longer-than-normal.
+  For example, if you allocate multiple objects in the
+  innermost part of a for-loop during each frame of an alpha
+  blending animation, you might pollute your memory heap with a
+  lot of objects.
+  In that circumstance, the garbage collector executes multiple garbage
+  collection events and can degrade the performance of your app.
+</p>
+
+<p>
+  For more general information about garbage collection, see
+  <a href="https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)"
+  class="external-link">Garbage collection</a>.
+</p>
+
+<!-- Section 2 #################################################### -->
+
+<h2 id="SharingRAM">Sharing Memory</h2>
+
+<p>
+  In order to fit everything it needs in RAM,
+  Android tries to share RAM pages across processes. It
+  can do so in the following ways:
+</p>
+
+<ul>
+  <li>
+    Each app process is forked from an existing process called Zygote.
+    The Zygote process starts when the system boots and loads common
+    framework code and resources
+    (such as activity themes). To start a new app process,
+    the system forks the Zygote process then
+    loads and runs the app's code in the new process.
+    This approach allows most of the RAM pages allocated for
+    framework code and resources to be shared across all app processes.
+  </li>
+
+  <li>
+    Most static data is mmapped into a process.
+    This technique allows data to be shared
+    between processes, and also allows it to be paged
+    out when needed. Example static data include:
+    Dalvik code (by placing it in a pre-linked <code>.odex</code>
+    file for direct mmapping), app resources
+    (by designing the resource table to be a structure
+    that can be mmapped and by aligning the zip
+    entries of the APK), and traditional project
+    elements like native code in <code>.so</code> files.
+  </li>
+
+  <li>
+    In many places, Android shares the same dynamic
+    RAM across processes using explicitly allocated
+    shared memory regions (either with ashmem or gralloc).
+    For example, window surfaces use shared
+    memory between the app and screen compositor, and
+    cursor buffers use shared memory between the
+    content provider and client.
+  </li>
+</ul>
+
+<p>
+  Due to the extensive use of shared memory, determining
+  how much memory your app is using requires
+  care. Techniques to properly determine your app's
+  memory use are discussed in
+  <a href="{@docRoot}studio/profile/investigate-ram.html">Investigating Your RAM Usage</a>.
+</p>
+
+<!-- Section 3 #################################################### -->
+
+<h2 id="AllocatingRAM">Allocating and Reclaiming App Memory</h2>
+
+<p>
+  The Dalvik heap is constrained to a
+  single virtual memory range for each app process. This defines
+  the logical heap size, which can grow as it needs to
+  but only up to a limit that the system defines
+  for each app.
+</p>
+
+<p>
+  The logical size of the heap is not the same as
+  the amount of physical memory used by the heap.
+  When inspecting your app's heap, Android computes
+  a value called the Proportional Set Size (PSS),
+  which accounts for both dirty and clean pages
+  that are shared with other processes—but only in an
+  amount that's proportional to how many apps share
+  that RAM. This (PSS) total is what the system
+  considers to be your physical memory footprint.
+  For more information about PSS, see the
+  <a href="{@docRoot}studio/profile/investigate-ram.html">Investigating Your RAM Usage</a>
+  guide.
+</p>
+
+<p>
+  The Dalvik heap does not compact the logical
+  size of the heap, meaning that Android does not
+  defragment the heap to close up space. Android
+  can only shrink the logical heap size when there
+  is unused space at the end of the heap. However,
+  the system can still reduce physical memory used by the heap.
+  After garbage collection, Dalvik
+  walks the heap and finds unused pages, then returns
+  those pages to the kernel using madvise. So, paired
+  allocations and deallocations of large
+  chunks should result in reclaiming all (or nearly all)
+  the physical memory used. However,
+  reclaiming memory from small allocations can be much
+  less efficient because the page used
+  for a small allocation may still be shared with
+  something else that has not yet been freed.
+
+</p>
+
+<!-- Section 4 #################################################### -->
+
+<h2 id="RestrictingMemory">Restricting App Memory</h2>
+
+<p>
+  To maintain a functional multi-tasking environment,
+  Android sets a hard limit on the heap size
+  for each app. The exact heap size limit varies
+  between devices based on how much RAM the device
+  has available overall. If your app has reached the
+  heap capacity and tries to allocate more
+  memory, it can receive an {@link java.lang.OutOfMemoryError}.
+</p>
+
+<p>
+  In some cases, you might want to query the
+  system to determine exactly how much heap space you
+  have available on the current device—for example, to
+  determine how much data is safe to keep in a
+  cache. You can query the system for this figure by calling
+  {@link android.app.ActivityManager#getMemoryClass() }.
+  This method returns an integer indicating the number of
+  megabytes available for your app's heap.
+</p>
+
+<!-- Section 5 #################################################### -->
+
+<h2 id="SwitchingApps">Switching apps</h2>
+
+<p>
+  When users switch between apps,
+  Android keeps apps that
+  are not foreground&mdash;that is, not visible to the user or running a
+  foreground service like music playback&mdash;
+  in a least-recently used (LRU) cache.
+  For example, when a user first launches an app,
+  a process is created for it; but when the user
+  leaves the app, that process does <em>not</em> quit.
+  The system keeps the process cached. If
+  the user later returns to the app, the system reuses the process, thereby
+  making the app switching faster.
+</p>
+
+<p>
+  If your app has a cached process and it retains memory
+  that it currently does not need,
+  then your app&mdash;even while the user is not using it&mdash;
+  affects the system's
+  overall performance. As the system runs low on memory,
+  it kills processes in the LRU cache
+  beginning with the process least recently used. The system also
+  accounts for processes that hold onto the most memory
+  and can terminate them to free up RAM.
+</p>
+
+<p class="note">
+  <strong>Note:</strong> When the system begins killing processes in the
+  LRU cache, it primarily works bottom-up. The system also considers which
+  processes consume more memory and thus provide the system
+  more memory gain if killed.
+  The less memory you consume while in the LRU list overall,
+  the better your chances are
+  to remain in the list and be able to quickly resume.
+</p>
+
+<p>
+  For more information about how processes are cached while
+  not running in the foreground and how
+  Android decides which ones
+  can be killed, see the
+  <a href="{@docRoot}guide/components/processes-and-threads.html">Processes and Threads</a>
+  guide.
+</p>
diff --git a/docs/html/topic/performance/memory.jd b/docs/html/topic/performance/memory.jd
new file mode 100644
index 0000000..ef1c4ae
--- /dev/null
+++ b/docs/html/topic/performance/memory.jd
@@ -0,0 +1,593 @@
+page.title=Manage Your App's Memory
+page.tags=ram,low memory,OutOfMemoryError,onTrimMemory
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+<h2>In this document</h2>
+<ol>
+  <li><a href="#monitor">Monitor Available Memory and Memory Usage</a>
+    <ul>
+      <li><a href="#AnalyzeRam">Tools for analyzing RAM usage</a></li>
+      <li><a href="#release">Release memory in response to events</a></li>
+      <li><a href="#CheckHowMuchMemory">Check how much memory you should use</a></li>
+    </ul>
+  </li>
+  <li><a href="#code">Use More Efficient Code Constructs</a>
+    <ul>
+      <li><a href="#Services">Use services sparingly</a></li>
+      <li><a href="#DataContainers">Use optimized data containers</a></li>
+      <li><a href="#Abstractions">Be careful with code abstractions</a></li>
+      <li><a href="#NanoProto">Use nano protobufs for serialized data</a></li>
+      <li><a href="#churn">Avoid memory churn</a></li>
+    </ul>
+  </li>
+  <li><a href="#remove">Remove Memory-Intensive Resources and Libraries</a>
+    <ul>
+      <li><a href="#reduce">Reduce overall APK size</a></li>
+      <li><a href="#DependencyInjection">Avoid dependency injection frameworks</a></li>
+      <li><a href="#ExternalLibs">Be careful about using external libraries</a></li>
+    </ul>
+  </li>
+</ol>
+<h2>See Also</h2>
+<ul>
+  <li><a href="{@docRoot}training/articles/memory-overview.html">Overview of Android Memory Management</a>
+  </li>
+  <li><a href="{@docRoot}studio/profile/investigate-ram.html">Investigating Your RAM Usage</a>
+  </li>
+  <li><a href="{@docRoot}topic/performance/reduce-apk-size.html">Reduce APK Size</a></li>
+</ul>
+
+</div>
+</div>
+
+<!-- INTRO #################################################### -->
+
+<p>
+  Random-access memory (RAM) is a valuable
+  resource in any software development environment, but
+  it's even more valuable on a mobile operating system
+  where physical memory is often constrained.
+  Although both the Android Runtime (ART) and Dalvik virtual machine perform
+  routine garbage collection, this does not mean you can ignore
+  when and where your app allocates and releases memory.
+  You still need to avoid
+  introducing memory leaks, usually caused by holding onto
+  object references in static member variables, and
+  release any {@link java.lang.ref.Reference} objects at the appropriate
+  time as defined by
+  lifecycle callbacks.
+</p>
+
+<p>
+  This page explains how you can
+  proactively reduce memory usage within your app.
+  For more information about general
+  practices to clean up your resources when programming in Java,
+  refer to other books or online
+  documentation about managing resource references.
+  If you’re looking for information about how to
+  analyze memory in a running app, read
+  <a href="#AnalyzeRam">Tools for analyzing RAM usage</a>.
+  For more detailed information about how the Android Runtime and Dalvik
+  virtual machine manage memory, see the
+  <a href="{@docRoot}training/articles/memory-overview.html">Overview of Android Memory Management</a>.
+</p>
+
+<!-- Section 1 #################################################### -->
+
+<h2 id="monitor">Monitor Available Memory and Memory Usage</h2>
+
+<p>
+  The Android framework, Android Studio, and Android SDK
+  can help you analyze and adjust your app's memory usage.
+  The Android framework
+  exposes several APIs that allow your app to reduce its memory usage
+  dynamically during runtime. Android Studio and the Android SDK
+  contain several tools  that allow you to investigate how your
+  app uses memory.
+</p>
+
+<!-- Section 1.1 #################################################### -->
+
+<h3 id="AnalyzeRam">Tools for analyzing RAM usage</h3>
+
+<p>
+  Before you can fix the memory usage problems in your app, you first need
+  to find them. Android Studio and the Android SDK include several tools
+  for analyzing memory usage in your app:
+</p>
+
+<ol>
+  <li>
+    The Device Monitor has a Dalvik Debug Monitor Server (DDMS) tool that allows
+    you to inspect memory allocation within your app process.
+    You can use this information to understand how your
+    app uses memory overall. For example, you can force a garbage collection
+    event and then view the types of objects that remain in memory. You can
+    use this information to identify operations or actions within your app
+    that allocate or leave excessive amounts of objects in memory.
+
+    <p>For more information about how to use the DDMS tool, see
+      <a href="/studio/profile/ddms.html">Using DDMS</a>.
+    </p>
+  </li>
+
+  <li>
+    The Memory Monitor in Android Studio shows you how your app allocates
+    memory over the course of a single session.
+    The tool shows a graph of available
+    and allocated Java memory over time, including garbage collection events.
+    You can also initiate garbage collection events and take a snapshot of
+    the Java heap while your app runs. The output from the Memory Monitor tool
+    can help you identify points when your app experiences excessive garbage
+    collection events, leading to app slowness.
+    <p>
+      For more information about how to use Memory Monitor tool, see
+      <a href="{@docRoot}tools/debugging/debugging-memory.html#ViewHeap">Viewing Heap Updates</a>.
+    </p>
+  </li>
+
+  <li>
+    Garbage collection events also show up in the Traceview viewer. Traceview
+    allows you to view trace log files as both a timeline and as a profile
+    of what happened within a method. You can use this tool to determine
+    what code was executing when a garbage collection event occurred.
+    <p>
+      For more information about how to use the Traceview viewer, see
+      <a href="https://developer.android.com/studio/profile/traceview.html">Profiling with Traceview and dmtracedump</a>.
+    </p>
+  </li>
+
+  <li>
+    The Allocation Tracker tool in Android Studio gives you a detailed look
+    at how your app allocates memory.
+    The Allocation Tracker records an app's memory allocations and lists
+    all allocated objects within the profiling snapshot. You can use this
+    tool to track down parts of your code that allocate too many objects.
+
+    <p>
+      For more information about how to use the Allocation Tracker tool, see
+      <a href="{docRoot}studio/profile/allocation-tracker-walkthru.html">Allocation Tracker Walkthrough</a>.
+    </p>
+  </li>
+
+</ol>
+
+<!-- Section 1.2 #################################################### -->
+
+<h3 id="release">Release memory in response to events</h3>
+
+<p>
+  An Android device can run with varying amounts of free memory
+  depending on the physical amount of RAM on the device and how the user
+  operates it. The system broadcasts signals to indicate when it is under
+  memory pressure, and apps should listen for these signals and adjust
+  their memory usage as appropriate.
+</p>
+
+</p>
+  You can use the {@link android.content.ComponentCallbacks2} API
+  to listen for these signals and then adjust your memory
+  usage in response to app lifecycle
+  or device events. The
+  {@link android.content.ComponentCallbacks2#onTrimMemory onTrimMemory()}
+  method allows your app to listen for memory related events when the app runs
+  in the foreground (is visible) and when it runs in the background.
+</p>
+
+<p>
+  To listen for these events, implement the {@link
+  android.content.ComponentCallbacks2#onTrimMemory onTrimMemory()}
+  callback in your {@link android.app.Activity}
+  classes, as shown in the following code snippet.
+</p>
+
+<pre class="prettyprint">
+import android.content.ComponentCallbacks2;
+// Other import statements ...
+
+public class MainActivity extends AppCompatActivity
+    implements ComponentCallbacks2 {
+
+    // Other activity code ...
+
+    /**
+     * Release memory when the UI becomes hidden or when system resources become low.
+     * @param level the memory-related event that was raised.
+     */
+    public void onTrimMemory(int level) {
+
+        // Determine which lifecycle or system event was raised.
+        switch (level) {
+
+            case ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN:
+
+                /*
+                   Release any UI objects that currently hold memory.
+
+                   The user interface has moved to the background.
+                */
+
+                break;
+
+            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_MODERATE:
+            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW:
+            case ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL:
+
+                /*
+                   Release any memory that your app doesn't need to run.
+
+                   The device is running low on memory while the app is running.
+                   The event raised indicates the severity of the memory-related event.
+                   If the event is TRIM_MEMORY_RUNNING_CRITICAL, then the system will
+                   begin killing background processes.
+                */
+
+                break;
+
+            case ComponentCallbacks2.TRIM_MEMORY_BACKGROUND:
+            case ComponentCallbacks2.TRIM_MEMORY_MODERATE:
+            case ComponentCallbacks2.TRIM_MEMORY_COMPLETE:
+
+                /*
+                   Release as much memory as the process can.
+
+                   The app is on the LRU list and the system is running low on memory.
+                   The event raised indicates where the app sits within the LRU list.
+                   If the event is TRIM_MEMORY_COMPLETE, the process will be one of
+                   the first to be terminated.
+                */
+
+                break;
+
+            default:
+                /*
+                  Release any non-critical data structures.
+
+                  The app received an unrecognized memory level value
+                  from the system. Treat this as a generic low-memory message.
+                */
+                break;
+        }
+    }
+}
+</pre>
+
+<p>
+  The
+  {@link android.content.ComponentCallbacks2#onTrimMemory onTrimMemory()}
+  callback was added in Android 4.0 (API level 14). For earlier versions,
+  you can use the
+  {@link android.content.ComponentCallbacks#onLowMemory()}
+  callback as a fallback for older versions, which is roughly equivalent to the
+  {@link android.content.ComponentCallbacks2#TRIM_MEMORY_COMPLETE} event.
+</p>
+
+<!-- Section 1.3 #################################################### -->
+
+<h3 id="CheckHowMuchMemory">Check how much memory you should use</h3>
+
+<p>
+  To allow multiple running processes, Android sets a hard limit
+  on the heap size alloted for each app. The exact heap size limit varies
+  between devices based on how much RAM the device
+  has available overall. If your app has reached the heap capacity and
+  tries to allocate more
+  memory, the system throws an {@link java.lang.OutOfMemoryError}.
+</p>
+
+<p>
+  To avoid running out of memory, you can to query the system to determine
+  how much heap space you have available on the current device.
+  You can query the system for this figure by calling
+  {@link android.app.ActivityManager#getMemoryInfo(android.app.ActivityManager.MemoryInfo) getMemoryInfo()}.
+  This returns an
+  {@link android.app.ActivityManager.MemoryInfo } object that provides
+  information about the device's
+  current memory status, including available memory, total memory, and
+  the memory threshold&mdash;the memory level below which the system begins
+  to kill processes. The
+  {@link android.app.ActivityManager.MemoryInfo } class also exposes a simple
+  boolean field,
+  {@link android.app.ActivityManager.MemoryInfo#lowMemory }
+  that tells you whether the device is running low on memory.
+</p>
+
+<p>
+  The following code snippet shows an example of how you can use the
+  {@link android.app.ActivityManager#getMemoryInfo(android.app.ActivityManager.MemoryInfo) getMemoryInfo()}.
+  method in your application.
+</p>
+
+<pre class="prettyprint">
+public void doSomethingMemoryIntensive() {
+
+    // Before doing something that requires a lot of memory,
+    // check to see whether the device is in a low memory state.
+    ActivityManager.MemoryInfo memoryInfo = getAvailableMemory();
+
+    if (!memoryInfo.lowMemory) {
+        // Do memory intensive work ...
+    }
+}
+
+// Get a MemoryInfo object for the device's current memory status.
+private ActivityManager.MemoryInfo getAvailableMemory() {
+    ActivityManager activityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
+    ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
+    activityManager.getMemoryInfo(memoryInfo);
+    return memoryInfo;
+}
+</pre>
+
+<!-- Section 2 #################################################### -->
+
+<h2 id="code">Use More Memory-Efficient Code Constructs</h2>
+
+<p>
+  Some Android features, Java classes, and code constructs tend to
+  use more memory than others. You can minimize how
+  much memory your app uses by choosing more efficient alternatives in
+  your code.
+</p>
+
+<!-- Section 2.1 #################################################### -->
+
+<h3 id="Services">Use services sparingly</h3>
+
+<p>
+  Leaving a service running when it’s not needed is
+  <strong>one of the worst memory-management
+  mistakes</strong> an Android app can make. If your app needs a
+  <a href="{@docRoot}guide/components/services.html">service</a>
+  to perform work in the background, do not keep it running unless
+  it needs to run a job. Remember to stop your service when it has completed
+  its task. Otherwise, you can inadvertently cause a memory leak.
+</p>
+
+<p>
+  When you start a service, the system prefers to always keep the process
+  for that service running. This behavior
+  makes services processes very expensive
+  because the RAM used by a service remains unavailable to other processes.
+  This reduces the number of cached processes that the system can keep in
+  the LRU cache, making app switching less efficient. It can even lead to
+  thrashing in the system when memory is tight and the system can’t
+  maintain enough processes to host all the services currently running.
+</p>
+
+<p>
+  You should generally avoid use of persistent services because of
+  the on-going demands they place on available memory. Instead, we
+  recommend that you use an alternative implementation
+  such as {@link android.app.job.JobScheduler}. For more information about
+  how to use {@link android.app.job.JobScheduler} to schedule background
+  processes, see
+  <a href="/topic/performance/background-optimization.html">Background Optimizations</a>.
+<p>
+  If you must use a service, the
+  best way to limit the lifespan of your service is to use an {@link
+  android.app.IntentService}, which finishes
+  itself as soon as it's done handling the intent that started it.
+  For more information, read
+  <a href="{@docRoot}training/run-background-service/index.html">Running in a Background Service</a>.
+</p>
+
+<!-- Section 2.2 #################################################### -->
+
+<h3 id="DataContainers">Use optimized data containers</h3>
+
+<p>
+  Some of the classes provided by the programming language are not optimized for
+  use on mobile devices. For example, the generic
+  {@link java.util.HashMap} implementation can be quite memory
+  inefficient because it needs a separate entry object for every mapping.
+</p>
+
+<p>
+  The Android framework includes several optimized data containers, including
+  {@link android.util.SparseArray}, {@link android.util.SparseBooleanArray},
+  and {@link android.support.v4.util.LongSparseArray}.
+  For example, the {@link android.util.SparseArray} classes are more
+  efficient because they avoid the system's need to
+  <acronym title="Automatic conversion from primitive types to object classes (such as int to Integer)">autobox</acronym>
+  the key and sometimes value (which creates yet another object or
+  two per entry).
+</p>
+
+<p>
+  If necessary, you can always switch to raw arrays for a really lean data
+  structure.
+</p>
+
+<!-- Section 2.3 #################################################### -->
+
+<h3 id="Abstractions">Be careful with code abstractions</h3>
+
+<p>
+  Developers often use abstractions simply as a good programming practice,
+  because abstractions can improve code flexibility and maintenance.
+  However, abstractions come at a significant cost:
+  generally they require a fair amount more code that
+  needs to be executed, requiring more time and
+  more RAM for that code to be mapped into memory.
+  So if your abstractions aren't supplying a
+  significant benefit, you should avoid them.
+</p>
+
+<p>
+  For example, enums often require more than twice as much memory as static
+  constants. You should strictly avoid using enums on Android.
+</p>
+
+<!-- Section 2.4 #################################################### -->
+
+<h3 id="NanoProto">Use nano protobufs for serialized data</h3>
+
+<p>
+  <a href="https://developers.google.com/protocol-buffers/docs/overview">Protocol buffers</a>
+  are a language-neutral, platform-neutral, extensible mechanism
+  designed by Google for serializing structured data&mdash;similar to XML, but
+  smaller, faster, and simpler. If you decide to use
+  protobufs for your data, you should always use nano protobufs in your
+  client-side code. Regular protobufs generate extremely verbose code, which
+  can cause many kinds of problems in your app such as
+  increased RAM use, significant APK size increase, and slower execution.
+</p>
+
+<p>
+  For more information, see the "Nano version" section in the
+  <a href="https://android.googlesource.com/platform/external/protobuf/+/master/java/README.txt"
+class="external-link">protobuf readme</a>.
+</p>
+
+<!-- Section 2.5 #################################################### -->
+
+<h3 id="churn">Avoid memory churn</h3>
+
+<p>
+  As mentioned previously, garbage collections events don't normally affect
+  your app's performance. However, many garbage collection events that occur
+  over a short period of time can quickly eat up your frame time. The more time
+  that the system spends on garbage collection, the less time it has to do
+  other stuff like rendering or streaming audio.
+</p>
+
+<p>
+  Often, <em>memory churn</em> can cause a large number of
+  garbage collection events to occur. In practice, memory churn describes the
+  number of allocated temporary objects that occur in a given amount of time.
+</p>
+
+<p>
+  For example, you might allocate multiple temporary objects within a
+  <code>for</code> loop. Or you might create new
+  {@link android.graphics.Paint} or {@link android.graphics.Bitmap}
+  objects inside the
+  {@link android.view.View#onDraw(android.graphics.Canvas) onDraw()}
+  function of a view.
+  In both cases, the app creates a lot of objects quickly at high volume.
+  These can quickly consume all the available memory in the young generation,
+  forcing a garbage collection event to occur.
+</p>
+
+<p>
+  Of course, you need to find the places in your code where
+  the memory churn is high before you can fix them. Use the tools discussed in
+  <a href="#AnalyzeRam">Analyze your RAM usage</a>
+</p>
+
+<p>
+  Once you identify the problem areas in your code, try to reduce the number of
+  allocations within performance critical areas. Consider moving things out of
+  inner loops or perhaps moving them into a
+  <a href="https://en.wikipedia.org/wiki/Factory_method_pattern" class="external-link">Factory</a>
+  based allocation structure.
+</p>
+
+<!-- Section 3 #################################################### -->
+
+<h2 id="remove">Remove Memory-Intensive Resources and Libraries</h2>
+
+<p>
+  Some resources and libraries within your code can gobble up memory without
+  you knowing it. Overall size of your APK, including third-party libraries
+  or embedded resources, can affect how much memory your app consumes. You can
+  improve your app's memory consumption by removing any redundant, unnecessary,
+  or bloated components, resources, or libraries from your code.
+</p>
+
+<!-- Section 3.1 #################################################### -->
+
+<h3 id="reduce">Reduce overall APK size</h3>
+
+<p>
+  You can significantly reduce your app's memory usage by reducing the overall
+  size of your app. Bitmap size, resources, animation frames, and third-party
+  libraries can all contribute to the size of your APK.
+  Android Studio and the Android SDK provide multiple tools
+  to help you reduce the size of your resources and external dependencies.
+</p>
+
+<p>
+  For more information about how to reduce your overall APK size, see
+  <a href="{@docRoot}topic/performance/reduce-apk-size.html">Reduce APK Size</a>.
+</p>
+
+<!-- Section 3.2 #################################################### -->
+
+<h3 id="DependencyInjection">Use caution with dependency injection frameworks</h3>
+
+<p>
+  Dependency injection framework such as
+  <a href="https://code.google.com/p/google-guice/" class="external-link">Guice</a>
+  or
+  <a href="https://github.com/roboguice/roboguice" class="external-link">RoboGuice</a>
+  can simplify the code you write and provide an adaptive environment
+  that's useful for testing and other configuration changes. However, dependency
+  frameworks aren't always optimized for mobile devices.
+</p>
+
+<p>
+  For example, these frameworks tend to initialize processes by
+  scanning your code for annotations. This which can require significant
+  amounts of your code to be mapped into RAM unnecessarily. The system
+  allocates these mapped pages into clean memory so Android can drop them; yet
+  that can't happen until the pages have remained in memory for a long period
+  of time.
+ </p>
+
+<p>
+  If you need to use a dependency injection framework in your app, consider
+  using
+  <a class="external-link" href="http://google.github.io/dagger/">Dagger</a>
+  instead. For example, Dagger does not use reflection to scan your app's code.
+  Dagger's strict implementation means that it can be used in Android apps
+  without needlessly increasing memory usage.
+</p>
+
+<!-- Section 3.3 #################################################### -->
+
+<h3 id="ExternalLibs">Be careful about using external libraries</h3>
+
+<p>
+  External library code is often not written for mobile environments and
+  can be inefficient when used
+  for work on a mobile client. When you decide to use an
+  external library, you may need to optimize that library for mobile devices.
+  Plan for that work up-front and analyze the library in terms of code size and
+  RAM footprint before deciding to use it at all.
+</p>
+
+<p>
+  Even some mobile-optimized libraries can cause problems due to differing
+  implementations. For example, one library may use nano protobufs
+  while another uses micro protobufs, resulting in two different protobuf
+  implementations in your app. This can happen with different
+  implementations of logging, analytics, image loading frameworks,
+  caching, and many other things you don't expect.
+</p>
+
+<p>
+  Although <a href="{@docRoot}tools/help/proguard.html">ProGuard</a> can
+  help to remove APIs and resources with the right flags, it can't remove a
+  library's large internal dependencies. The features that you want in these
+  libraries may require lower-level dependencies. This becomes especially
+  problematic when you use an {@link android.app.Activity } subclass from a
+  library (which will tend to have wide swaths of dependencies),
+  when libraries use reflection (which is common and means you need to spend a
+  lot of time manually tweaking ProGuard to get it to work), and so on.
+</p>
+
+<p>
+  Also avoid using a shared library for just one or two features out of dozens.
+  You don't want to pull in a large amount of code and overhead that
+  you don't even use. When you consider whether to use a library, look for
+  an implementation that strongly matches what you need. Otherwise, you might
+  decide to create your own implementation.
+</p>
+
diff --git a/docs/html/topic/performance/network-xfer.jd b/docs/html/topic/performance/network-xfer.jd
new file mode 100644
index 0000000..7fe5594
--- /dev/null
+++ b/docs/html/topic/performance/network-xfer.jd
@@ -0,0 +1,374 @@
+page.title=Reducing Image Download Sizes
+page.metaDescription=Improve network performance by optimizing image size.
+
+meta.tags="performance"
+page.tags="performance"
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+<h2>In this document</h2>
+    <ol>
+
+      <li>
+        <a href="#uif">Understanding Image Formats</a>
+        <ul>
+           <li><a href="#png">PNG</a></li>
+           <li><a href="#jpg">JPG</a></li>
+           <li><a href="#webp">WebP</a></li>
+        </ul>
+      </li>
+      <li>
+        <a href="#sf">Selecting a Format</a></li>
+      <li><a href="#doqv">Determining Optimal Quality Values</a>
+        <ul>
+           <li><a href="#sv">Scalar Values (JPG, WebP only)</a></li>
+           <li><a href="#butter">Butteraugli</a></li>
+        </ul>
+      </li>
+      <li><a href="#sizes">Serving Sizes</a></li>
+    </ol>
+  </div>
+</div>
+
+<p>
+Most download traffic consists of images. As a result, the smaller you can make
+your downloadable images, the better a network experience your app can provide
+for users. This page provides guidance on making image files smaller and more
+network-friendly.
+</p>
+
+<h2 id="uif">Understanding Image Formats</h2>
+
+<p>Android apps typically use images that are in one or more of the following file
+formats: PNG, JPG, and WebP. For each of these formats, there are steps you can
+take to reduce image sizes.
+</p>
+
+<h3 id="png">PNG</h3>
+
+<p>
+A key to making your PNG files smaller is reducing the number of unique
+colors used in each row of pixels that comprises the image. By using fewer
+colors, you improve the compression potential at all of the other stages of
+the pipeline.
+</p>
+
+<p>
+Reducing the number of unique colors makes a significant difference because PNG
+compression effectiveness is partly a function of the degree to which
+horizontally adjacent pixel colors vary. Thus, reducing the number of unique
+colors in each row of your PNG images can help in reducing their file sizes.
+</p>
+
+<p>
+When deciding whether to pursue this strategy, you should keep in mind that
+reducing the number of unique colors effectively amounts to applying a lossy
+encoding stage to the image. However, an encoding tool may not be a good
+judge of how bad a seemingly small error looks to the human eye. Therefore,
+you should perform this work manually in order to help ensure
+the right balance between efficient compression and acceptable image quality.
+</p>
+
+<p>
+There are two particularly useful approaches you can take: striving for indexed
+formats, and applying vector quantization.
+</p>
+
+
+<h4 id="strive">Strive for indexed formats</h4>
+
+<p>
+Any attempt at color reduction should start with trying to optimize your colors
+so that you can use the INDEXED format when exporting the image as a PNG. The
+INDEXED color mode works by choosing the best 256 colors to use, and replacing
+all pixel values with indices into that color palette. The result is a
+reduction from 16 million (potential) colors to only 256 colors: from 3 (without
+transparency) or 4 (with transparency) bytes per pixel to 1 byte per pixel.
+This change is a significant first-step file size reduction.
+</p>
+
+<p>
+Figure 1 shows shows an image and its indexed variant.
+</p>
+
+  <img src="{@docRoot}topic/performance/images/beforeafterindexed.png">
+  <p class="img-caption">
+Figure 1. An image before and after conversion to the INDEXED format.
+  </p>
+
+
+<p>
+Figure 2 shows the color palette for the image in Figure 1:
+</p>
+
+  <img src="{@docRoot}topic/performance/images/palette.png">
+  <p class="img-caption">
+Figure 2. The color palette for the image in Figure 1.
+  </p>
+
+<p>
+Representing your image as a paletted image goes a long way toward
+significantly improving the file size, so it's worth investigating if the
+majority of your images can be converted.
+</p>
+
+<p>
+Of course, not every image can be accurately represented with only 256 colors.
+Some images, for example, might need 257, 310, 512, or 912 colors to
+look correct. In such cases, vector quantization can also be helpful.
+</p>
+
+<h4 id="vq">Vector quantization</h4>
+
+<p>
+The process of creating an indexed image may be better described as vector
+quantization (VQ). VQ serves as a rounding process for multidimensional
+numbers.  In this process, all the colors in your image get grouped based upon
+their similarity. For a given group, all colors in that group are replaced by a
+single <em>center point</em> value, which minimizes error for colors in that
+cell (or "site" if you're using the Voronoi terminology). In Figure 3,
+the green dots represent input colors, and the red dots are the center points
+that replace the input colors. Each cell is bounded by blue lines.
+</p>
+
+  <img src="{@docRoot}topic/performance/images/vq.gif">
+  <p class="img-caption">
+Figure 3. Applying vector quantization to the colors in an image.
+</p>
+
+<p>
+The result of applying VQ to an image reduces the number of unique colors,
+replacing each group of colors with a single color that's "pretty close"
+in visual quality.
+</p>
+
+<p>
+This technique also allows you to define the maximum number of unique colors in
+your image. For example, Figure 4 shows the a parrot head in 16.7 million colors
+(24 bits per pixel, or bpp) alongside a version that only allows only
+16 (3 bpp) unique colors to be used.
+</p>
+
+  <img src="{@docRoot}topic/performance/images/parrot.png">
+  <p class="img-caption">
+Figure 4. Image before and after application of vector quantification.
+  </p>
+
+<p>
+Immediately, you can see that there's a loss of quality; most of the gradient
+colors have been replaced, imparting a banding effect to the image. This image
+needs more than 16 unique colors.
+</p>
+
+<p>
+Setting up a VQ step in your pipeline can help you get a better sense of the
+true number of unique colors that your image uses, and can help you reduce them
+significantly. There are a number of readily available tools that you can use
+to help you implement this technique.
+</p>
+
+<h3 id="jpg">JPG</h3>
+
+<p>
+If you are using JPG images, there are several small changes you can make that
+potentially provide significant file-size savings. These include:
+</p>
+
+<ul>
+   <li>
+Producing a smaller file size through different encoding methods (without
+impacting quality).
+   </li>
+
+   <li>
+Adjusting quality slightly in order to yield better compression.
+   </li>
+</ul>
+
+<p>Pursuing these strategies can often net you file-size reductions of up to
+25%.
+</p>
+
+<p>
+When choosing tools, remember that photo exporting tools can
+insert unnecessary metadata, such as GPS information, into your images. At
+a minimum, try to leverage existing tools to help strip out this information
+from your files.
+</p>
+
+<h3 id="webp">WebP</h3>
+
+<p>
+WebP is a newer image format supported from Android 4.2.1 (API level 17). This
+format provides superior lossless and lossy compression for images on the web.
+Using WebP, developers can create smaller, richer images. WebP lossless image
+files are, on average,
+<a href="https://developers.google.com/speed/webp/docs/webp_lossless_alpha_study#conclusions">
+26% smaller</a> than PNGs. These image files also support
+transparency (also known as alpha channel) at a cost of just
+<a href="https://developers.google.com/speed/webp/docs/webp_lossless_alpha_study#results">
+22% more</a> bytes.
+</p>
+
+<p>
+WebP lossy images are
+<a href="https://developers.google.com/speed/webp/docs/webp_study#experiment_1_webp_vs_jpeg_at_equal_ssim_index">
+25-34% smaller</a> than comparable JPG images at equivalent
+<a href="https://en.wikipedia.org/wiki/Structural_similarity">SSIM</a>
+quality indices. For cases when lossy RGB compression is acceptable, lossy
+WebP also supports transparency, typically producing file sizes 3 times smaller
+than PNG.
+</p>
+
+<p>
+For more information about WebP, visit the
+<a href="https://developers.google.com/speed/webp/">WebP site</a>.
+</p>
+
+<h2 id="sf">Selecting a Format</h2>
+
+<p>
+Different image formats are suitable for different types of images. JPG and PNG
+have very different compression processes, and they produce quite different
+results.
+</p>
+
+<p>
+The decision between PNG and JPG often comes down to the complexity of the
+image itself. Figure 5 shows two images that come out quite differently
+depending on which compression scheme the developer applies. The image on the
+left has many small details, and thus compresses more efficiently with JPG. The
+image on the right, with runs of the same color, compresses more efficiently
+with PNG.
+</p>
+
+  <img src="{@docRoot}topic/performance/images/comparison.png">
+  <p class="img-caption">
+Figure 5. Suitable cases for JPG vs. PNG
+  </p>
+
+
+<p>
+WebP as a format can support both lossy and lossless modes, making it an ideal
+replacement for both PNG and JPG. The only thing to keep in mind is that it
+only has native support on devices running Android 4.2.1 (API level 17) and
+higher. Fortunately, the large
+<a
+href="https://developer.android.com/about/dashboards/index.html#Platform">
+majority of devices</a> satisfy that requirement.
+</p>
+
+<p>
+Figure 6 provides a simple visualization to help you decide which compression
+scheme to use.
+</p>
+
+  <img src="{@docRoot}topic/performance/images/decisions.png">
+  <p class="img-caption">
+Figure 6. Deciding on a compression scheme
+  </p>
+
+<h2 id="doqv">Determining Optimal Quality Values</h2>
+
+<p>
+There are several techniques you can use to achieve the right balance between
+compression and image quality. One technique uses scalar values and therefore
+only works for JPG and WebP. The other technique takes advantage of the
+Butteraugli library, and is usable for all image formats.
+</p>
+
+<h3 id="sv">Scalar values (JPG and WebP only)</h3>
+
+<p>
+The power of JPG and WebP comes from the fact that you can use a scalar value
+to balance quality against file size. The trick is finding out what the correct
+quality value is for your image. Too low a quality level produces a small file
+at the cost of image quality. Too high a quality level increases file size
+without providing a noticeable benefit to the user.
+</p>
+
+<p>
+The most straightforward solution is to pick some non-maximum value, and use
+that value. However, be aware that the quality value affects every image
+differently. While a quality of 75%, for example, may look fine on most images,
+there may be some cases do not fare as well. You should make sure to test your
+chosen maximum value against a representative sample of images. Also, make
+sure to perform all of your tests against the original images, and not on
+compressed versions.
+</p>
+
+<p>
+For large media applications that upload and re-send millions of JPGs a day,
+hand-tuning for each asset is impractical. You might address this challenge by
+specifying several different quality levels, according to image category. For
+example, you might set 35% as the quality setting for thumbnails, since a
+smaller image hides more compression artifacts.
+</p>
+
+<h3 id="butter">Butteraugli</h4>
+
+<p>
+The Butteraugli project is a library to test an image's Psychovisual Error
+Threshold: the point at which a viewer starts to notice image degradation. In
+other words, this project attempts to quantify how distorted your compressed
+image is.
+</p>
+
+<p>
+Butteraugli allows you to define a goal for visual quality, and then run PNG,
+JPG, WebP lossy, and WebP lossless compressions. You can then choose the image
+that is the best balance of file size and Butteraugli level. Figure 7 shows an
+example of how Butteraugli was used to find the minimal JPG quality level
+before the visual distortion was high enough for a user could perceive a
+problem; the result is a roughly 65% reduction in file size.
+</p>
+
+  <img src="{@docRoot}topic/performance/images/moarparrots.png">
+  <p class="img-caption">
+Figure 7. An image before and after application of Butteraugli technology.
+  </p>
+
+<p>
+Butteraugli allows you to proceed based on either output or input. That is, you
+can look for the lowest quality setting before a user perceives noticeable
+distortion in the resulting image, or you can iteratively set image-distortion
+levels to learn their associated quality levels.
+</p>
+
+<h2 id="sizes">Serving Sizes</h2>
+
+<p>
+It is tempting to keep only a single resolution of an image on a server. When a
+device accesses the image, the server serves it at that one resolution and
+leaves downscaling to the device.
+</p>
+
+<p>
+This solution is convenient for the developer, but potentially painful for the
+user, because the solution forces the user to download much more data than they
+need.
+
+You should instead store multiple sizes of images, and serve the size that is
+most appropriate for a particular use case. For example, for a thumbnail,
+serving an actual thumbnail image instead of serving and downscaling a
+full-size version consumes much less network bandwidth
+</p>
+
+</p>
+This approach is good for download speed, and is less costly for users who may
+be using limited or metered data plans. Proceeding like this also results in
+the image's taking less space on the device and in main memory. In the
+case of large images, such as 4K ones, this approach also saves the device
+from having to resize images before loading them.
+</p>
+
+<p>
+Implementing this approach requires that you have a backend image service to
+provide images at various resolutions with proper caching. There are existing
+services that can provide help with this task. For example,
+<a href="https://cloud.google.com/appengine/">App Engine</a> comes
+with image resizing functionality already installed.
+</p>
diff --git a/docs/html/topic/performance/power/battery-historian.jd b/docs/html/topic/performance/power/battery-historian.jd
new file mode 100644
index 0000000..79ea59d
--- /dev/null
+++ b/docs/html/topic/performance/power/battery-historian.jd
@@ -0,0 +1,247 @@
+page.title=Analyzing Power Use with Battery Historian
+page.metaDescription=Improve network performance by optimizing image size.
+
+meta.tags="power"
+page.tags="power"
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+<h2>In this document</h2>
+    <ol>
+      <li>
+        <a href="#sv">System-wide View</a>
+      </li>
+      <li>
+        <a href="#asd">App-Specific Data</a>
+      </li>
+      <li>
+        <a href="#usecases">Other Cases Where Battery Historian Can Help</a>
+      </li>
+    </ol>
+<h2>See also</h2>
+   <ol>
+      <li>
+      <a href="https://github.com/google/battery-historian">Battery Historian
+      on GitHub</a>
+      </li>
+
+      <li>
+      <a href="https://developer.android.com/studio/profile/battery-historian.html">
+      Batterystats and Battery Historian Walkthrough
+      </li>
+
+      <li>
+      <a href="https://youtu.be/VC2Hlb22mZM?list=PLOU2XLYxmsILe6_eGvDN3GyiodoV3qNSC&t=2063"
+      target="_blank">
+      Battery Historian talk at Google I/O 2016</a>
+      </li>
+    </ol>
+  </div>
+</div>
+
+<p>
+The Battery Historian tool provides insight into a device’s battery consumption
+over time. At a system-wide level, the tool visualizes power-related events from
+the system logs in an HTML representation. At an app-specific level, the tool
+provides a variety of data that can help you identify battery-draining app
+behavior.
+</p>
+
+<p>
+This document describes some of the ways you can use Battery Historian
+to learn about battery-consumption patterns. The document begins by explaining
+how to read the system-wide data that Battery Historian reports. Then,
+it presents ways in which you can use Battery Historian to diagnose
+and troubleshoot your own app's behavior related to battery consumption.
+Last, it offers several tips on scenarios in which Battery Historian may be
+particularly useful.
+</p>
+
+<h2 id="sv">System-wide View</h2>
+
+<p>
+The Battery Historian tool provides a system-wide visualization of various
+app and system behaviors, along with their correlation against battery
+consumption over time. This view, shown in Figure 1, can help you
+diagnose and identify power use issues with your app.
+</p>
+
+  <img src="{@docRoot}topic/performance/images/generic-timeline.png">
+  <p class="img-caption">
+<strong>Figure 1.</strong>
+Battery Historian’s display of system-wide events affecting power
+consumption.
+  </p>
+
+<p>
+Of particular interest in this figure is the black, horizontal, downward trend
+line representing Battery Level, measured on the y-axis. For example, at the
+very beginning of the Battery Level line, at approximately 6:50 AM, the
+visualization shows a relatively steep drop in battery level.
+</p>
+
+<p>
+Figure 2 provides a close-up of that part of the display.
+</p>
+
+  <img src="{@docRoot}topic/performance/images/s-generic-closeup.png">
+  <p class="img-caption">
+<strong>Figure 2.</strong>
+A close-up of the Battery Historian timeline from roughly 6:50 AM to 7:20 AM.
+  </p>
+
+<p>
+At the very beginning of the Battery Level line, as battery decline steeply,
+the display shows three things happening: The CPU is running, an app has
+acquired a wakelock, and the screen is on. In this way, Battery Historian helps
+you understand what events are happening when battery consumption is high. You
+can then target these behaviors in your app and investigate whether there are
+related optimizations you can make.
+</p>
+
+<p>
+The system-wide visualization can provide other clues, as well. For instance, if
+it shows that the mobile radio is frequently being turned off and on, there may
+be an opportunity to optimize this behavior through <a href=”intelligent
+scheduling page”>intelligent scheduling APIs</a> such as JobScheduler or
+Firebase Job Dispatcher.
+</p>
+
+<p>
+The next section explains how to investigate behavior and events specific to
+your own app.
+</p>
+
+<p>
+<h2 id="asd">App-Specific Data</h2>
+</p>
+
+<p>
+In addition to the macro-level data provided by the system-wide view, Battery
+Historian also provides tables and some visualization of data specific to each
+app running on your device. The tabular data includes:
+</p>
+
+<ul>
+   <li>The app’s estimated power use on the device.</li>
+   <li>Network information.</li>
+   <li>Wakelocks.</li>
+   <li>Services.</li>
+   <li>Process info.</li>
+</ul>
+
+<p>
+The tables provide two dimensions of data about your app. First, you can look
+up where your app’s power usage ranks compared to other apps. To do so, click
+<em>Device Power Estimates</em> table under <em>Tables</em>. This example
+examines a fictional app called Pug Power.
+</p>
+
+  <img src="{@docRoot}topic/performance/images/app-rankings.png">
+  <p class="img-caption">
+<strong>Figure 3.</strong> Investigating which apps consume the most power.
+  </p>
+
+<p>
+The table in Figure 3 reveals that Pug Power is the ninth biggest consumer of
+battery power on this device, and the third biggest app that is not part of the
+OS. This data suggests that this app bears deeper investigation.
+</p>
+
+<p>
+To look up the data for a specific app, enter its package name into the lower
+of the two dropdown menus under <em>App Selection</em>, located under the left
+side of the visualization.
+</p>
+
+  <img src="{@docRoot}topic/performance/images/dropdown.png">
+  <p class="img-caption">
+<strong>Figure 4.</strong> Entering a specific app whose data to view.
+  </p>
+
+<p>
+When you select a specific app, the following data visualization categories
+change to display app-specific data instead of system-wide data:
+</p>
+
+<ul>
+   <li>SyncManager.</li>
+   <li>Foreground process.</li>
+   <li>Userspace Wakelock.</li>
+   <li>Top app.</li>
+   <li>JobScheduler.</li>
+   <li>Activity Manager Proc.</li>
+</ul>
+
+The SyncManager and JobScheduler visualizations immediately make it obvious if
+your app performs syncs and executes jobs more frequently than necessary. In
+doing so, they can quickly reveal an opportunity to optimize your app’s
+behavior for improved battery performance.
+
+<p>
+You can also obtain one more piece of app-specific visualization data,
+<em>Userspace Wakelock</em>. To include this information in the bug report,
+enter the following command in your terminal window:
+</p>
+
+<pre>
+$ adb shell dumpsys batterystats --enable full-wake-history
+</pre>
+
+<p class="note">
+<strong>Note:</strong> From Android 6.0 (API level 23), the platform includes
+Doze functionality, which imposes certain optimizations on apps. For example,
+Doze batches jobs to take place during brief maintenance windows, regardless of
+how JobScheduler has scheduled them.
+</p>
+
+<p>
+Figures 5 and 6 show data for Pug Power: Figure 5
+shows the visualization of
+the app-specific data, and Figure 6 shows the corresponding tabular data.
+</p>
+
+  <img src="{@docRoot}topic/performance/images/pug-visualization.png">
+  <p class="img-caption">
+<strong>Figure 5.</strong> Visualization of data for fictional app Pug Power.
+  </p>
+
+  <img src="{@docRoot}topic/performance/images/pugspecificdata.png">
+  <p class="img-caption">
+<strong>Figure 6.</strong> Tabular data for the fictional Pug Power app.
+  </p>
+
+<p>
+A look at the visualization does not show anything immediately obvious.
+The JobScheduler line shows that the app has no jobs scheduled. The SyncManager
+line shows that the app has not performed any syncs.
+</p>
+
+<p>
+However, examination of the <em>Wakelocks</em> segment of the tabular data
+reveals that Pug Power acquires wakelocks totaling over an hour. This unusual
+and costly behavior can account for the app’s high level of power consumption.
+This piece of information helps the developer target an area where optimization
+is likely to greatly help. In this case, why does the app acquire so much
+wakelock time, and how can the developer ameliorate this behavior?
+</p>
+
+<h2 id="usecases">Other Cases Where Battery Historian Can Help</h2>
+
+<p>
+There are many other cases in which Battery Historian can help you diagnose
+opportunities for improving battery behavior. For example, Battery Historian
+can tell you if your app is:
+</p>
+
+<ul>
+   <li>Firing wakeup alarms overly frequently (every 10 seconds or less).</li>
+   <li>Continuously holding a GPS lock.</li>
+   <li>Scheduling jobs every 30 seconds or less.</li>
+   <li>Scheduling syncs every 30 seconds or less.</li>
+   <li>Using the cellular radio more frequently than you expect.</li>
+</ul>
+
diff --git a/docs/html/topic/performance/power/index.jd b/docs/html/topic/performance/power/index.jd
new file mode 100644
index 0000000..88addce
--- /dev/null
+++ b/docs/html/topic/performance/power/index.jd
@@ -0,0 +1,125 @@
+page.title=Optimizing for Battery Life
+page.metaDescription=Learn how to help your app go easier on the battery.
+
+meta.tags="performance"
+page.tags="performance"
+
+@jd:body
+
+<div id="qv-wrapper">
+  <div id="qv">
+    <h2>
+      In this document
+    </h2>
+    <ol>
+      <li>
+        <a href="#lazy">Lazy First</a>
+      </li>
+      <li>
+        <a href="#features">Platform Features</a>
+      </li>
+      <li>
+        <a href="#toolery">Tooling</a>
+      </li>
+    </ol>
+  </div>
+</div>
+
+<p>Battery life is the single most important aspect of the mobile user
+experience. A device without power offers no functionality at all.
+For this reason, it is critically important that apps be as respectful of
+battery life as possible.</p>
+
+<p>There are three important things to keep in mind in keeping your app
+power-thrifty:</p>
+<ul>
+<li>Make your apps <em>Lazy First</em>.</li>
+<li>Take advantage of platform features that can help manage your app's battery
+consumption.</li>
+<li>Use tools that can help you identify battery-draining culprits.</li>
+</ul>
+
+<h2 id="lazy">Lazy First</h2>
+
+<p>Making your app Lazy First means looking for ways to reduce and optimize
+operations that are particularly battery-intensive. The core questions
+underpinning Lazy First design are:
+
+<ul>
+
+   <li><strong>Reduce:</strong> Are there redundant operations your app can cut
+out? For example, can it cache downloaded data instead of repeatedly waking
+   up the radio to re-download the data?</li>
+
+   <li><strong>Defer:</strong> Does an app need to perform an action right
+   away? For example,
+    can it wait until the device is charging before it backs data up to the
+    cloud?</li>
+
+   <li><strong>Coalesce:</strong> Can work be batched, instead of putting the
+   device
+   into an active state many times? For example, is it really necessary for
+   several dozen apps to each turn on the radio at separate times to send
+   their messages? Can the messages instead be transmitted during a
+   single awakening of the radio?</li>
+</ul>
+
+<p>
+You should ask these questions when it comes to using the CPU,
+the radio, and the screen. Lazy First design is often a good way
+to tame these battery killers.
+</p>
+
+<p>
+To help you achieve these and other efficiencies, the Android platform
+provides a number of features to help maximize battery life.
+</p>
+
+<h2 id="features">Platform Features</h2>
+
+<p>
+Broadly speaking, the Android platform provides two categories of help
+for you to optimize your app's battery use. First, it provides several
+APIs that you can implement in your app. You can learn more about these APIs in
+<a href="/topic/performance/scheduling.html">Intelligent Job Scheduling</a>
+and <a href="/performance/power/network/index.html">
+Network Use and Battery Consumption</a>.
+</p>
+
+<p>
+There are also internal mechanisms in the platform to help conserve
+battery life. While they are not APIs that you implement programmatically,
+you should still be aware of them so that your app can leverage them
+successfully. For more information, see
+<a href="/training/monitoring-device-state/doze-standby.html">Doze and
+App Standby</a>.</p>
+
+<p>
+You can get even more benefit out of these features by using the tools
+available for the platform to discover the parts of your app that consume
+the most power. Finding what to target is a big step toward
+successful optimization.
+</p>
+
+<h2 id ="toolery">Tooling</h2>
+
+<p>There are tools for Android, including
+<a href="/studio/profile/dev-options-rendering.html">Profile GPU Rendering</a>
+and <a class="external-link"
+href="https://github.com/google/battery-historian">Battery Historian</a>
+to help you identify areas that you can optimize for better battery life.
+Take advantage of these tools to target areas where you can apply the
+principles of Lazy First.
+</p>
+
+<section class="dac-section dac-small" id="latest-games"><div class="wrap">
+  <h2 class="norule" style="margin:0 0">More resources</h2>
+  <div class="resource-widget resource-flow-layout col-16"
+       data-query="collection:develop/performance/landing"
+       data-sortOrder="random"
+       data-cardSizes="6x6"
+       data-maxResults="24"
+       data-items-per-page="24"
+       data-initial-results="3"></div>
+  </div>
+</section>
diff --git a/docs/html/topic/performance/rendering/index.jd b/docs/html/topic/performance/rendering/index.jd
new file mode 100644
index 0000000..1b16df0
--- /dev/null
+++ b/docs/html/topic/performance/rendering/index.jd
@@ -0,0 +1,60 @@
+page.title=Rendering
+page.article=true
+
+page.tags=battery
+page.metaDescription=Learn how to optimize your app's rendering performance.
+
+@jd:body
+
+
+<iframe width="448" height="252"
+    src="//www.youtube.com/embed/wIy8g8yNhNk?autohide=1&amp;showinfo=0"
+    frameborder="0" allowfullscreen=""
+    style="float: right; margin: 0 0 20px 20px;"></iframe>
+
+<p>
+  A key aspect of your app that influences your users' perception of quality is
+  the smoothness with which it renders images and text to the screen. It is
+  important to avoid jank and sluggish responsiveness when your app is drawing
+  to the screen.
+</p>
+
+<p>
+  This section helps you learn several ways to optimize your app's rendering
+  performance: reducing overdraw, optimizing view hierarchies, and taking
+  advantage of the Profile GPU tool.
+</p>
+
+<h2>Rendering Actions</h2>
+
+<dl>
+  <dt>
+    <strong><a href="overdraw.html">
+        Reducing Overdraw</a></strong>
+  </dt>
+  <dd>
+    Minimize the number of times you app redraws the same pixel in a single
+    frame.
+  </dd>
+
+  <dt>
+    <strong><a href="optimizing-view-hierarchies.html">
+        Performance and View Hierarchies</a></strong>
+  </dt>
+  <dd>
+    Make sure your layout and measurement are executing efficiently, and
+    avoid double taxation.
+  </dd>
+
+
+  <dt>
+    <strong><a href="profile-gpu.html">
+        Analyzing with Profile GPU Rendering</a></strong>
+  </dt>
+  <dd>
+    Take advantage of this on-device tool to identify bottlenecks that
+    may be slowing your app's rendering down.
+  </dd>
+
+
+</dl>
diff --git a/docs/html/topic/performance/optimizing-view-hierarchies.jd b/docs/html/topic/performance/rendering/optimizing-view-hierarchies.jd
similarity index 100%
rename from docs/html/topic/performance/optimizing-view-hierarchies.jd
rename to docs/html/topic/performance/rendering/optimizing-view-hierarchies.jd
diff --git a/docs/html/topic/performance/rendering/overdraw.jd b/docs/html/topic/performance/rendering/overdraw.jd
new file mode 100644
index 0000000..c1feff5
--- /dev/null
+++ b/docs/html/topic/performance/rendering/overdraw.jd
@@ -0,0 +1,197 @@
+page.title=Reducing Overdraw
+page.metaDescription=Improve performance by reducing unnecessary rendering.
+
+meta.tags="performance"
+page.tags="performance"
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+<h2>In this document</h2>
+    <ol>
+
+      <li>
+        <a href="#understanding">Understanding Overdraw</a>
+      </li>
+      <li>
+        <a href="#finding">Finding Overdraw Problems</a>
+      </li>
+      <li>
+        <a href="#fixing">Fixing Overdraw</a>
+      </li>
+    </ol>
+  </div>
+</div>
+
+<p>
+An app may draw the same pixel more than once within a single frame, an event
+called <em>overdraw</em>. Overdraw is usually unnecessary, and best
+eliminated. It manifests itself as a performance problem by wasting GPU time to
+render pixels that don't contribute to what the user sees on the screen.
+</p>
+
+<p>
+This document explains overdraw: what it is, how to diagnose it, and actions you
+can take to eliminate or mitigate it.
+</p>
+
+<h2 name="understanding">Understanding Overdraw</h2>
+
+<p>
+Overdraw refers to the system's drawing a pixel on the screen multiple times
+in a single frame of rendering. For example, if we have a bunch of stacked UI
+cards, each card hides a portion of the one below it.
+</p>
+
+<p>
+However, the system still needs to draw even the hidden portions of the cards
+in the stack. This is because stacked cards are rendered according to the
+<a class="external-link"
+href="https://en.wikipedia.org/wiki/Painter%27s_algorithm">painter's
+algorithm</a>: that is, in back-to-front order.
+This sequence of rendering allows the system to apply proper alpha blending to
+translucent objects such as shadows.
+</p>
+
+<h2 name="finding">Finding Overdraw Problems</h2>
+
+<p>
+The platform offers several tools to help you determine if overdraw is
+affecting your app's performance. These tools are available right on the device,
+and accessible by turning on <strong>Developer Settings</strong></a> under
+<em>Settings</em>. For more information about device developer settings, see
+<a href="/studio/run/device.html#developer-device-options">Run Apps on a
+Hardware Device</a>.
+</p>
+
+<h3 id="dgot">Debug GPU overdraw tool</h3>
+
+<p>
+The Debug GPU Overdraw tool uses color-coding to show the number of times your
+app draws each pixel on the screen. The higher this count, the
+more likely it is that overdraw affects your app's performance.
+</p>
+
+<p>
+For more information on how to use the tool, refer to the related
+<a href="/studio/profile/dev-options-overdraw.html">walkthrough</a>
+and
+<a href="https://io2015codelabs.appspot.com/codelabs/android-performance-debug-gpu-overdraw#1">
+codelab</a>.
+</p>
+
+<h3 id="pgrt">Profile GPU rendering tool</h3>
+
+<p>
+The Profile GPU Rendering tool displays, as a scrolling histogram, the time
+each stage of the rendering pipeline takes to display a single frame. The
+<em>Process</em> part of each bar, indicated in orange, shows when the system
+is swapping buffers; this metric provides important clues about overdraw.
+</p>
+
+<p>
+On less performant GPUs, available fill-rate (the speed at which the GPU can
+fill the frame buffer) can be quite low. As the number of
+pixels required to draw a frame increases, the GPU may take longer to process
+new commands, and ask the rest of the system to wait until it can catch up.
+The <em>Process</em> bar shows that this spike happens as the GPU gets
+overwhelmed trying to draw pixels as fast as possible. Issues other than
+raw numbers of pixels may also cause this metric to spike. For example,
+if the Debug GPU Overdraw tool shows heavy overdraw and <em>Process</em> spikes,
+there's likely an issue with overdraw.
+</p>
+
+<p class="note"><strong>Note: </strong>The
+<a href="https://developer.android.com/studio/profile/dev-options-rendering.html">
+Profile GPU Rendering</a> tool does not
+work with apps that use the NDK. This is because the system pushes framework
+messages to the background whenever OpenGL takes a full-screen context. In
+such cases, you may find a profiling tool provided by the GPU manufacturer
+helpful.</p>
+
+<h2 name="fixing">Fixing Overdraw</h2>
+
+<p>
+There are several strategies you can pursue to reduce or eliminate overdraw:
+</p>
+
+<ul>
+   <li>Removing unneeded backgrounds in layouts.</li>
+   <li>Flattening the view hierarchy.</li>
+   <li>Reducing transparency.</li>
+</ul>
+
+<p>
+This section provides information about each of these approaches.
+</p>
+
+<h3 id="rubil">Removing unneeded backgrounds in layouts</h3>
+
+<p>
+By default, a layout does not have a background, which means it does not render
+anything directly by itself. When layouts do have backgrounds, however, they may
+contribute to overdraw.
+</p>
+
+<p>
+Removing unnecessary backgrounds is a quick way of improving rendering
+performance. An unnecessary background may never be visible because it's
+completely covered by everything else the app is drawing on top of that
+view. For example, the system may entirely cover up a parent's
+background when it draws child views on top of it.
+</p>
+
+<p>
+To find out why you're overdrawing, walk through the hierarchy in
+the <a href="/studio/profile/hierarchy-viewer.html">Hierarchy Viewer</a> tool.
+As you do so, look out for any backgrounds you can eliminate because
+they are not visible to the user. Cases where many containers share a
+common background color offer another opportunity to eliminate unneeded
+backgrounds: You can set the window background to the main background color
+of your app, and leave all of the containers above it with no background values
+defined.
+</p>
+
+<h3 id="fvh">Flattening view hierarchy</h3>
+
+<p>
+Modern layouts make it easy to stack and layer views to produce beautiful
+design. However, doing so can degrade performance by resulting in overdraw,
+especially in scenarios where each stacked view object is opaque, requiring the
+drawing of both seen and unseen pixels to the screen.
+</p>
+
+<p>
+If you encounter this sort of issue, you may be able to improve performance by
+optimizing your view hierarchy to reduce the number of overlapping UI objects.
+For more information about how to accomplish this, see
+<a href="/topic/performance/optimizing-view-hierarchies.html">Optimizing View
+Hierarchies</a>.
+</p>
+
+<h3 id="rt">Reducing transparency</h3>
+
+<p>
+Rendering of transparent pixels on screen, known as alpha rendering, is a key
+contributor to overdraw. Unlike standard overdraw,
+in which the system completely hides existing drawn pixels by drawing
+opaque pixels on top of them, transparent
+objects require existing pixels to be drawn first, so that the right blending
+equation can occur.  Visual effects like transparent animations, fade-outs, and
+drop shadows all involve some sort of transparency, and can therefore contribute
+significantly to overdraw. You can improve overdraw in these situations by
+reducing the number of transparent objects you render. For example, you can get
+gray text by drawing black text in a {@link android.widget.TextView} with a
+translucent alpha value set on it. But you can get the same effect with far
+better performance by simply drawing the text in gray.
+</p>
+
+<p>
+To learn more about performance costs that transparency imposes throughout the
+entire drawing pipeline, watch the video
+<a href="https://www.youtube.com/watch?v=wIy8g8yNhNk&index=46&list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE">
+Hidden Costs of Transparency</a>.
+</p>
+
diff --git a/docs/html/topic/performance/rendering/profile-gpu.jd b/docs/html/topic/performance/rendering/profile-gpu.jd
new file mode 100644
index 0000000..fc98777
--- /dev/null
+++ b/docs/html/topic/performance/rendering/profile-gpu.jd
@@ -0,0 +1,406 @@
+page.title=Analyzing with Profile GPU Rendering
+page.metaDescription=Use the Profile GPU tool to help you optimize your app's rendering performance.
+
+meta.tags="power"
+page.tags="power"
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+
+<h2>In this document</h2>
+    <ol>
+      <li>
+        <a href="#visrep">Visual Representation</a></li>
+      </li>
+
+      <li>
+       <a href="#sam">Stages and Their Meanings</a>
+      
+      <ul>
+         <li>
+           <a href="#sv">Input Handling</a>
+         </li>
+         <li>
+           <a href="#asd">Animation</a>
+         </li>
+         <li>
+           <a href="#asd">Measurement/Layout</a>
+         </li>
+         <li>
+           <a href="#asd">Drawing</a>
+         </li>
+         </li>
+         <li>
+           <a href="#asd">Sync/Upload</a>
+         </li>
+         <li>
+           <a href="#asd">Issuing Commands</a>
+         </li>
+         <li>
+           <a href="#asd">Processing/Swapping Buffer</a>
+         </li>
+         <li>
+           <a href="#asd">Miscellaneous</a>
+         </li>
+      </ul>
+      </li>     
+     </ol>
+  </div>
+</div>
+
+<p>
+The <a href="/studio/profile/dev-options-rendering.html">
+Profile GPU Rendering</a> tool indicates the relative time that each stage of
+the rendering pipeline takes to render the previous frame. This knowledge
+can help you identify bottlenecks in the pipeline, so that you
+can know what to optimize to improve your app's rendering performance.
+</p>
+
+<p>
+This page briefly explains what happens during each pipeline stage, and
+discusses issues that can cause bottlenecks there. Before reading
+this page, you should be familiar with the information presented in the
+<a href="/studio/profile/dev-options-rendering.html">Profile GPU
+Rendering Walkthrough</a>. In addition, to understand how all of the
+stages fit together, it may be helpful to review
+<a href="https://www.youtube.com/watch?v=we6poP0kw6E&index=64&list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE">
+how the rendering pipeline works.</a>
+</p>
+
+<h2 id="#visrep">Visual Representation</h2>
+
+<p>
+The Profile GPU Rendering tool displays stages and their relative times in the
+form of a graph: a color-coded histogram. Figure 1 shows an example of
+such a display.
+</p>
+
+  <img src="{@docRoot}topic/performance/images/bars.png">
+  <p class="img-caption">
+<strong>Figure 1.</strong> Profile GPU Rendering Graph
+  </p>
+
+</p>
+
+<p>
+Each segment of each vertical bar displayed in the Profile GPU Rendering
+graph represents a stage of the pipeline and is highlighted using a specific
+color in
+the bar graph. Figure 2 shows a key to the meaning of each displayed color.
+</p>
+
+  <img src="{@docRoot}topic/performance/images/s-profiler-legend.png">
+  <p class="img-caption">
+<strong>Figure 2.</strong> Profile GPU Rendering Graph Legend
+  </p>
+
+<p>
+Once you understand what each color signfiies,
+you can target specific aspects of your
+app to try to optimize its rendering performance.
+</p>
+
+<h2 id="sam">Stages and Their Meanings</a></h2>
+
+<p>
+This section explains what happens during each stage corresponding
+to a color in Figure 2, as well as bottleneck causes to look out for.
+</p>
+
+
+<h3 id="ih">Input Handling</h3>
+
+<p>
+The input handling stage of the pipeline measures how long the app
+spent handling input events. This metric indicates how long the app
+spent executing code called as a result of input event callbacks.
+</p>
+
+<h4>When this segment is large</h4>
+
+<p>
+High values in this area are typically a result of too much work, or
+too-complex work, occurring inside the input-handler event callbacks.
+Since these callbacks always occur on the main thread, solutions to this
+problem focus on optimizing the work directly, or offloading the work to a
+different thread.
+</p>
+
+<p>
+It’s also worth noting that {@link android.support.v7.widget.RecyclerView}
+scrolling can appear in this phase.
+{@link android.support.v7.widget.RecyclerView} scrolls immediately when it
+consumes the touch event. As a result,
+it can inflate or populate new item views. For this reason, it’s important to
+make this operation as fast as possible. Profiling tools like Traceview or
+Systrace can help you investigate further.
+</p>
+
+<h3 id="at">Animation</h3>
+
+<p>
+The Animations phase shows you just how long it took to evaluate all the
+animators that were running in that frame. The most common animators are
+{@link android.animation.ObjectAnimator},
+{@link android.view.ViewPropertyAnimator}, and
+<a href="/training/transitions/overview.html">Transitions</a>.
+</p>
+
+<h4>When this segment is large</h4>
+
+<p>
+High values in this area are typically a result of work that’s executing due
+to some property change of the animation. For example, a fling animation,
+which scrolls your {@link android.widget.ListView} or
+{@link android.support.v7.widget.RecyclerView}, causes large amounts of view
+inflation and population.
+</p>
+
+<h3 id="ml">Measurement/Layout</h3>
+
+<p>
+In order for Android to draw your view items on the screen, it executes
+two specific operations across layouts and views in your view hierarchy.
+</p>
+
+<p>
+First, the system measures the view items. Every view and layout has
+specific data that describes the size of the object on the screen. Some views
+can have a specific size; others have a size that adapts to the size
+of the parent layout container
+</p>
+
+<p>
+Second, the system lays out the view items. Once the system calculates
+the sizes of children views, the system can proceed with layout, sizing
+and positioning the views on the screen.
+</p>
+
+<p>
+The system performs measurement and layout not only for the views to be drawn,
+but also for the parent hierarchies of those views, all the way up to the root
+view.
+</p>
+
+<h4>When this segment is large</h4>
+
+<p>
+If your app spends a lot of time per frame in this area, it is
+usually either because of the sheer volume of views that need to be
+laid out, or problems such as
+<a href="/topic/performance/optimizing-view-hierarchies.html#double">
+double taxation</a> at the wrong spot in your
+hierarchy. In either of these cases, addressing performance involves
+<a href="/topic/performance/optimizing-view-hierarchies.html">improving
+the performance of your view hierarchies</a>.
+</p>
+
+<p>
+Code that you’ve added to
+{@link android.view.View#onLayout(boolean, int, int, int, int)} or
+{@link android.view.View#onMeasure(int, int)}
+can also cause performance
+issues. <a href="/studio/profile/traceview.html">Traceview</a> and
+<a href="/studio/profile/systrace.html">Systrace</a> can help you examine
+the callstacks to identify problems your code may have.
+</p>
+
+<h3 id="draw">Drawing</h3>
+
+<p>
+The draw stage translates a view’s rendering operations, such as drawing
+a background or drawing text, into a sequence of native drawing commands.
+The system captures these commands into a display list.
+</p>
+
+<p>
+The Draw bar records how much time it takes to complete capturing the commands
+into the display list, for all the views that needed to be updated on the screen
+this frame. The measured time applies to any code that you have added to the UI
+objects in your app. Examples of such code may be the
+{@link android.view.View#onDraw(android.graphics.Canvas) onDraw()},
+{@link android.view.View#dispatchDraw(android.graphics.Canvas) dispatchDraw()},
+and the various <code>draw ()methods</code> belonging to the subclasses of the
+{@link android.graphics.drawable.Drawable} class.
+</p>
+
+<h4>When this segment is large</h4>
+
+<p>
+In simplified terms, you can understand this metric as showing how long it took
+to run all of the calls to
+{@link android.view.View#onDraw(android.graphics.Canvas) onDraw()}
+for each invalidated view. This
+measurement includes any time spent dispatching draw commands to children and
+drawables that may be present. For this reason, when you see this bar spike, the
+cause could be that a bunch of views suddenly became invalidated. Invalidation
+makes it necessary to regenerate views' display lists. Alternatively, a
+lengthy time may be the result of a few custom views that have some extremely
+complex logic in their
+{@link android.view.View#onDraw(android.graphics.Canvas) onDraw()} methods.
+</p>
+
+<h3 id="su">Sync/Upload</h3>
+
+<p>
+The Sync & Upload metric represents the time it takes to transfer
+bitmap objects from CPU memory to GPU memory during the current frame.
+</p>
+
+<p>
+As different processors, the CPU and the GPU have different RAM areas
+dedicated to processing. When you draw a bitmap on Android, the system
+transfers the bitmap to GPU memory before the GPU can render it to the
+screen. Then, the GPU caches the bitmap so that the system doesn’t need to
+transfer the data again unless the texture gets evicted from the GPU texture
+cache.
+</p>
+
+<p class="note"><strong>Note:</strong> On Lollipop devices, this stage is
+purple.
+</p>
+
+<h4>When this segment is large</h4>
+
+<p>
+All resources for a frame need to reside in GPU memory before they can be
+used to draw a frame. This means that a high value for this metric could mean
+either a large number of small resource loads or a small number of very large
+resources. A common case is when an app displays a single bitmap that’s
+close to the size of the screen. Another case is when an app displays a
+large number of thumbnails.
+</p>
+
+<p>
+To shrink this bar, you can employ techniques such as:
+</p>
+
+<ul>
+   <li>
+Ensuring your bitmap resolutions are not much larger than the size at which they
+will be displayed. For example, your app should avoid displaying a 1024x1024
+image as a 48x48 image.
+   </li>
+
+   <li>
+Taking advantage of {@link android.graphics.Bitmap#prepareToDraw()}
+to asynchronously pre-upload a bitmap before the next sync phase.
+   </li>
+</ul>
+
+<h3 id="ic">Issuing Commands</h3>
+
+<p>
+The <em>Issue Commands</em> segment represents the time it takes to issue all
+of the commands necessary for drawing display lists to the screen.
+</p>
+
+<p>
+For the system to draw display lists to the screen, it sends the
+necessary commands to the GPU. Typically, it performs this action through the
+<a href="/guide/topics/graphics/opengl.html">OpenGL ES</a> API.
+</p>
+
+<p>
+This process takes some time, as the system performs final transformation
+and clipping for each command before sending the command to the GPU. Additional
+overhead then arises on the GPU side, which computes the final commands. These
+commands include final transformations, and additional clipping.
+</p>
+
+<h4>When this segment is large</h4>
+
+<p>
+The time spent in this stage is a direct measure of the complexity and
+quantity of display lists that the system renders in a given
+frame. For example, having many draw operations, especially in cases where
+there's a small inherent cost to each draw primitive, could inflate this time.
+For example:
+</p>
+
+<pre>
+for (int i = 0; i < 1000; i++)
+canvas.drawPoint()
+</pre>
+
+<p>
+is a lot more expensive to issue than:
+</p>
+
+<pre>
+canvas.drawPoints(mThousandPointArray);
+</pre>
+
+<p>
+There isn’t always a 1:1 correlation between issuing commands and
+actually drawing display lists. Unlike <em>Issue Commands</em>,
+which captures the time it takes to send drawing commands to the GPU,
+the <em>Draw</em> metric represents the time that it took to capture the issued
+commands into the display list.
+</p>
+
+<p>
+This difference arises because the display lists are cached by
+the system wherever possible. As a result, there are situations where a
+scroll, transform, or animation requires the system to re-send a display
+list, but not have to actually rebuild it&mdash;recapture the drawing
+commands&mdash;from scratch. As a result, you can see a high “Issue
+commands” bar without seeing a high <em>Draw commands</em> bar.
+</p>
+
+<h3 id="psb">Processing/Swapping Buffers</h3>
+
+<p>
+Once Android finishes submitting all its display list to the GPU,
+the system issues one final command to tell the graphics driver that it's
+done with the current frame. At this point, the driver can finally present
+the updated image to the screen.
+</p>
+
+<h4>When this segment is large</h4>
+
+<p>
+It’s important to understand that the GPU executes work in parallel with the
+CPU. The Android system issues draw commands to the GPU, and then moves on to
+the next task. The GPU reads those draw commands from a queue and processes
+them.
+</p>
+
+<p>
+In situations where the CPU issues commands faster than the GPU
+consumes them, the communications queue between the processors can become
+full. When this occurs, the CPU blocks, and waits until there is space in the
+queue to place the next command. This full-queue state arises often during the
+<em>Swap Buffers</em> stage, because at that point, a whole frame’s worth of
+commands have been submitted.
+</p>
+
+</p>
+The key to mitigating this problem is to reduce the complexity of work occurring
+on the GPU, in similar fashion to what you would do for the “Issue Commands”
+phase.
+</p>
+
+
+<h3 id="mt">Miscellaneous</h3>
+
+<p>
+In addition to the time it takes the rendering system to perform its work,
+there’s an additional set of work that occurs on the main thread and has
+nothing to do with rendering. Time that this work consumes is reported as
+<em>misc time</em>. Misc time generally represents work that might be occurring
+on the UI thread between two consecutive frames of rendering.
+</p>
+
+<h4>When this segment is large</h4>
+
+<p>
+If this value is high, it is likely that your app has callbacks, intents, or
+other work that should be happening on another thread. Tools such as
+<a href="/studio/profile/traceview.html">Method
+Tracing</a> or <a href="/studio/profile/systrace.html">Systrace</a> can provide
+visibility into the tasks that are running on
+the main thread. This information can help you target performance improvements.
+</p>
diff --git a/docs/html/training/_book.yaml b/docs/html/training/_book.yaml
index e9635be..47862e2 100644
--- a/docs/html/training/_book.yaml
+++ b/docs/html/training/_book.yaml
@@ -438,16 +438,6 @@
       path: /training/efficient-downloads/redundant_redundant.html
     - title: Modifying Patterns Based on the Connectivity Type
       path: /training/efficient-downloads/connectivity_patterns.html
-  - title: Backing up App Data to the Cloud
-    path: /training/backup/index.html
-    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
-    - title: Using the Backup API
-      path: /training/backup/backupapi.html
   - title: Resolving Cloud Save Conflicts
     path: /training/cloudsave/conflict-res.html
     path_attributes:
@@ -1156,7 +1146,7 @@
         value: 维护兼容性
       - name: zh-tw-lang
         value: 維持相容性
-    - title: Selecting Colors with the Palette API 
+    - title: Selecting Colors with the Palette API
       path: /training/material/palette-colors.html
 
 - title: Best Practices for User Input
@@ -1242,15 +1232,9 @@
       path: /training/scheduling/wakelock.html
     - title: Scheduling Repeating Alarms
       path: /training/scheduling/alarms.html
-
 - title: Best Practices for Performance
   path: /training/best-performance.html
   section:
-  - title: Managing Your App's Memory
-    path: /training/articles/memory.html
-    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
     path_attributes:
@@ -1282,23 +1266,6 @@
     - 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
-      section:
-      - title: Collecting Network Traffic Data
-        path: /training/performance/battery/network/gather-data.html
-      - title: Analyzing Network Traffic Data
-        path: /training/performance/battery/network/analyze-data.html
-      - title: Optimizing User-Initiated Network Use
-        path: /training/performance/battery/network/action-user-traffic.html
-      - title: Optimizing App-Initiated Network Use
-        path: /training/performance/battery/network/action-app-traffic.html
-      - title: Optimizing Server-Initiated Network Use
-        path: /training/performance/battery/network/action-server-traffic.html
-      - title: Optimizing General Network Use
-        path: /training/performance/battery/network/action-any-traffic.html
-    - title: Optimizing for Doze and App Standby
-      path: /training/monitoring-device-state/doze-standby.html
     - title: Monitoring the Battery Level and Charging State
       path: /training/monitoring-device-state/battery-monitoring.html
       path_attributes:
diff --git a/docs/html/training/articles/memory.jd b/docs/html/training/articles/memory.jd
deleted file mode 100644
index de7af58..0000000
--- a/docs/html/training/articles/memory.jd
+++ /dev/null
@@ -1,740 +0,0 @@
-page.title=Managing Your App's Memory
-page.tags=ram,low memory,OutOfMemoryError,onTrimMemory
-page.article=true
-@jd:body
-
-
-<div id="tb-wrapper">
-<div id="tb">
-
-<h2>In this document</h2>
-<ol class="nolist">
-  <li><a href="#Android">How Android Manages Memory</a>
-    <ol>
-      <li><a href="#SharingRAM">Sharing Memory</a></li>
-      <li><a href="#AllocatingRAM">Allocating and Reclaiming App Memory</a></li>
-      <li><a href="#RestrictingMemory">Restricting App Memory</a></li>
-      <li><a href="#SwitchingApps">Switching Apps</a></li>
-    </ol>
-  </li>
-  <li><a href="#YourApp">How Your App Should Manage Memory</a>
-    <ol>
-      <li><a href="#Services">Use services sparingly</a></li>
-      <li><a href="#ReleaseMemoryAsUiGone">Release memory when your user interface becomes hidden</a></li>
-      <li><a href="#ReleaseMemoryAsTight">Release memory as memory becomes tight</a></li>
-      <li><a href="#CheckHowMuchMemory">Check how much memory you should use</a></li>
-      <li><a href="#Bitmaps">Avoid wasting memory with bitmaps</a></li>
-      <li><a href="#DataContainers">Use optimized data containers</a></li>
-      <li><a href="#Overhead">Be aware of memory overhead</a></li>
-      <li><a href="#Abstractions">Be careful with code abstractions</a></li>
-      <li><a href="#NanoProto">Use nano protobufs for serialized data</a></li>
-      <li><a href="#DependencyInjection">Avoid dependency injection frameworks</a></li>
-      <li><a href="#ExternalLibs">Be careful about using external libraries</a></li>
-      <li><a href="#OverallPerf">Optimize overall performance</a></li>
-      <li><a href="#Proguard">Use ProGuard to strip out any unneeded code</a></li>
-      <li><a href="#Zipalign">Use zipalign on your final APK</a></li>
-      <li><a href="#AnalyzeRam">Analyze your RAM usage</a></li>
-      <li><a href="#MultipleProcesses">Use multiple processes</a></li>
-    </ol>
-  </li>
-</ol>
-<h2>See Also</h2>
-<ul>
-  <li><a href="{@docRoot}tools/debugging/debugging-memory.html">Investigating Your RAM Usage</a>
-  </li>
-</ul>
-
-</div>
-</div>
-
-
-<p>Random-access memory (RAM) is a valuable resource in any software development environment, but
-it's even more valuable on a mobile operating system where physical memory is often constrained.
-Although Android's Dalvik virtual machine performs routine garbage collection, this doesn't allow
-you to ignore when and where your app allocates and releases memory.</p>
-
-<p>In order for the garbage collector to reclaim memory from your app, you need to avoid
-introducing memory leaks (usually caused by holding onto object references in global members) and
-release any {@link java.lang.ref.Reference} objects at the appropriate time (as defined by
-lifecycle callbacks discussed further below). For most apps, the Dalvik garbage collector takes
-care of the rest: the system reclaims your memory allocations when the corresponding objects leave
-the scope of your app's active threads.</p>
-
-<p>This document explains how Android manages app processes and memory allocation, and how you can
-proactively reduce memory usage while developing for Android. For more information about general
-practices to clean up your resources when programming in Java, refer to other books or online
-documentation about managing resource references. If you’re looking for information about how to
-analyze your app’s memory once you’ve already built it, read <a
-href="{@docRoot}tools/debugging/debugging-memory.html">Investigating Your RAM Usage</a>.</p>
-
-
-
-
-<h2 id="Android">How Android Manages Memory</h2>
-
-<p>Android does not offer swap space for memory, but it does use <a href=
-"http://en.wikipedia.org/wiki/Paging" class="external-link">paging</a> and <a href=
-"http://en.wikipedia.org/wiki/Memory-mapped_files" class="external-link">memory-mapping</a>
-(mmapping) to manage memory. This means that any memory you modify&mdash;whether by allocating
-new objects or touching mmapped pages&mdash;remains resident in RAM and cannot be paged out.
-So the only way to completely release memory from your app is to release object references you may
-be holding, making the memory available to the garbage collector. That is with one exception:
-any files mmapped in without modification, such as code, can be paged out of RAM if the system
-wants to use that memory elsewhere.</p>
-
-
-<h3 id="SharingRAM">Sharing Memory</h3>
-
-<p>In order to fit everything it needs in RAM, Android tries to share RAM pages across processes. It
-can do so in the following ways:</p>
-<ul>
-<li>Each app process is forked from an existing process called Zygote.
-The Zygote process starts when the system boots and loads common framework code and resources
-(such as activity themes). To start a new app process, the system forks the Zygote process then
-loads and runs the app's code in the new process. This allows most of the RAM pages allocated for
-framework code and resources to be shared across all app processes.</li>
-
-<li>Most static data is mmapped into a process. This not only allows that same data to be shared
-between processes but also allows it to be paged out when needed. Example static data include:
-Dalvik code (by placing it in a pre-linked {@code .odex} file for direct mmapping), app resources
-(by designing the resource table to be a structure that can be mmapped and by aligning the zip
-entries of the APK), and traditional project elements like native code in {@code .so} files.</li>
-
-<li>In many places, Android shares the same dynamic RAM across processes using explicitly allocated
-shared memory regions (either with ashmem or gralloc). For example, window surfaces use shared
-memory between the app and screen compositor, and cursor buffers use shared memory between the
-content provider and client.</li>
-</ul>
-
-<p>Due to the extensive use of shared memory, determining how much memory your app is using requires
-care. Techniques to properly determine your app's memory use are discussed in <a
-href="{@docRoot}tools/debugging/debugging-memory.html">Investigating Your RAM Usage</a>.</p>
-
-
-<h3 id="AllocatingRAM">Allocating and Reclaiming App Memory</h3>
-
-<p>Here are some facts about how Android allocates then reclaims memory from your app:</p>
-
-<ul>
-<li>The Dalvik heap for each process is constrained to a single virtual memory range. This defines
-the logical heap size, which can grow as it needs to (but only up to a limit that the system defines
-for each app).</li>
-
-<li>The logical size of the heap is not the same as the amount of physical memory used by the heap.
-When inspecting your app's heap, Android computes a value called the Proportional Set Size (PSS),
-which accounts for both dirty and clean pages that are shared with other processes&mdash;but only in an
-amount that's proportional to how many apps share that RAM. This (PSS) total is what the system
-considers to be your physical memory footprint. For more information about PSS, see the <a
-href="{@docRoot}tools/debugging/debugging-memory.html#ViewingAllocations">Investigating Your
-RAM Usage</a> guide.</li>
-
-<li>The Dalvik heap does not compact the logical size of the heap, meaning that Android does not
-defragment the heap to close up space. Android can only shrink the logical heap size when there
-is unused space at the end of the heap. But this doesn't mean the physical memory used by the heap
-can't shrink. After garbage collection, Dalvik walks the heap and finds unused pages, then returns
-those pages to the kernel using madvise. So, paired allocations and deallocations of large
-chunks should result in reclaiming all (or nearly all) the physical memory used. However,
-reclaiming memory from small allocations can be much less efficient because the page used
-for a small allocation may still be shared with something else that has not yet been freed.</li>
-</ul>
-
-
-<h3 id="RestrictingMemory">Restricting App Memory</h3>
-
-<p>To maintain a functional multi-tasking environment, Android sets a hard limit on the heap size
-for each app. The exact heap size limit varies between devices based on how much RAM the device
-has available overall. If your app has reached the heap capacity and tries to allocate more
-memory, it will receive an {@link java.lang.OutOfMemoryError}.</p>
-
-<p>In some cases, you might want to query the system to determine exactly how much heap space you
-have available on the current device&mdash;for example, to determine how much data is safe to keep in a
-cache. You can query the system for this figure by calling {@link
-android.app.ActivityManager#getMemoryClass()}. This returns an integer indicating the number of
-megabytes available for your app's heap. This is discussed further below, under
-<a href="#CheckHowMuchMemory">Check how much memory you should use</a>.</p>
-
-
-<h3 id="SwitchingApps">Switching Apps</h3>
-
-<p>Instead of using swap space when the user switches between apps, Android keeps processes that
-are not hosting a foreground ("user visible") app component in a least-recently used (LRU) cache.
-For example, when the user first launches an app, a process is created for it, but when the user
-leaves the app, that process does <em>not</em> quit. The system keeps the process cached, so if
-the user later returns to the app, the process is reused for faster app switching.</p>
-
-<p>If your app has a cached process and it retains memory that it currently does not need,
-then your app&mdash;even while the user is not using it&mdash;is constraining the system's
-overall performance. So, as the system runs low on memory, it may kill processes in the LRU cache
-beginning with the process least recently used, but also giving some consideration toward
-which processes are most memory intensive. To keep your process cached as long as possible, follow
-the advice in the following sections about when to release your references.</p>
-
-<p>More information about how processes are cached while not running in the foreground and how
-Android decides which ones
-can be killed is available in the <a href="{@docRoot}guide/components/processes-and-threads.html"
->Processes and Threads</a> guide.</p>
-
-
-
-
-<h2 id="YourApp">How Your App Should Manage Memory</h2>
-
-<p>You should consider RAM constraints throughout all phases of development, including during app
-design (before you begin development). There are many
-ways you can design and write code that lead to more efficient results, through aggregation of the
-same techniques applied over and over.</p>
-
-<p>You should apply the following techniques while designing and implementing your app to make it
-more memory efficient.</p>
-
-
-<h3 id="Services">Use services sparingly</h3>
-
-<p>If your app needs a <a href="{@docRoot}guide/components/services.html">service</a>
-to perform work in the background, do not keep it running unless
-it's actively performing a job. Also be careful to never leak your service by failing to stop it
-when its work is done.</p>
-
-<p>When you start a service, the system prefers to always keep the process for that service
-running. This makes the process very expensive because the RAM used by the service can’t be used by
-anything else or paged out. This reduces the number of cached processes that the system can keep in
-the LRU cache, making app switching less efficient. It can even lead to thrashing in the system
-when memory is tight and the system can’t maintain enough processes to host all the services
-currently running.</p>
-
-<p>The best way to limit the lifespan of your service is to use an {@link
-android.app.IntentService}, which finishes
-itself as soon as it's done handling the intent that started it. For more information, read
-<a href="{@docRoot}training/run-background-service/index.html">Running in a Background Service</a>
-.</p>
-
-<p>Leaving a service running when it’s not needed is <strong>one of the worst memory-management
-mistakes</strong> an Android app can make. So don’t be greedy by keeping a service for your app
-running. Not only will it increase the risk of your app performing poorly due to RAM constraints,
-but users will discover such misbehaving apps and uninstall them.</p>
-
-
-<h3 id="ReleaseMemoryAsUiGone">Release memory when your user interface becomes hidden</h3>
-
-<p>When the user navigates to a different app and your UI is no longer visible, you should
-release any resources that are used by only your UI. Releasing UI resources at this time can
-significantly increase the system's capacity for cached processes, which has a direct impact on the
-quality of the user experience.</p>
-
-<p>To be notified when the user exits your UI, implement the {@link
-android.content.ComponentCallbacks2#onTrimMemory onTrimMemory()} callback in your {@link
-android.app.Activity} classes. You should use this
-method to listen for the {@link android.content.ComponentCallbacks2#TRIM_MEMORY_UI_HIDDEN} level,
-which indicates your UI is now hidden from view and you should free resources that only your UI
-uses.</p>
-
-
-<p>Notice that your app receives the {@link android.content.ComponentCallbacks2#onTrimMemory
-onTrimMemory()} callback with {@link android.content.ComponentCallbacks2#TRIM_MEMORY_UI_HIDDEN}
-only when <em>all the UI components</em> of your app process become hidden from the user.
-This is distinct
-from the {@link android.app.Activity#onStop onStop()} callback, which is called when an {@link
-android.app.Activity} instance becomes hidden, which occurs even when the user moves to
-another activity in your app. So although you should implement {@link android.app.Activity#onStop
-onStop()} to release activity resources such as a network connection or to unregister broadcast
-receivers, you usually should not release your UI resources until you receive {@link
-android.content.ComponentCallbacks2#onTrimMemory onTrimMemory(TRIM_MEMORY_UI_HIDDEN)}. This ensures
-that if the user navigates <em>back</em> from another activity in your app, your UI resources are
-still available to resume the activity quickly.</p>
-
-
-
-<h3 id="ReleaseMemoryAsTight">Release memory as memory becomes tight</h3>
-
-<p>During any stage of your app's lifecycle, the {@link
-android.content.ComponentCallbacks2#onTrimMemory onTrimMemory()} callback also tells you when
-the overall device memory is getting low. You should respond by further releasing resources based
-on the following memory levels delivered by {@link android.content.ComponentCallbacks2#onTrimMemory
-onTrimMemory()}:</p>
-
-<ul>
-<li>{@link android.content.ComponentCallbacks2#TRIM_MEMORY_RUNNING_MODERATE}
-<p>Your app is running and not considered killable, but the device is running low on memory and the
-system is actively killing processes in the LRU cache.</p>
-</li>
-
-<li>{@link android.content.ComponentCallbacks2#TRIM_MEMORY_RUNNING_LOW}
-<p>Your app is running and not considered killable, but the device is running much lower on
-memory so you should release unused resources to improve system performance (which directly
-impacts your app's performance).</p>
-</li>
-
-<li>{@link android.content.ComponentCallbacks2#TRIM_MEMORY_RUNNING_CRITICAL}
-<p>Your app is still running, but the system has already killed most of the processes in the
-LRU cache, so you should release all non-critical resources now. If the system cannot reclaim
-sufficient amounts of RAM, it will clear all of the LRU cache and begin killing processes that
-the system prefers to keep alive, such as those hosting a running service.</p>
-</li>
-</ul>
-
-<p>Also, when your app process is currently cached, you may receive one of the following
-levels from {@link android.content.ComponentCallbacks2#onTrimMemory onTrimMemory()}:</p>
-<ul>
-<li>{@link android.content.ComponentCallbacks2#TRIM_MEMORY_BACKGROUND}
-<p>The system is running low on memory and your process is near the beginning of the LRU list.
-Although your app process is not at a high risk of being killed, the system may already be killing
-processes in the LRU cache. You should release resources that are easy to recover so your process
-will remain in the list and resume quickly when the user returns to your app.</p>
-</li>
-
-<li>{@link android.content.ComponentCallbacks2#TRIM_MEMORY_MODERATE}
-<p>The system is running low on memory and your process is near the middle of the LRU list. If the
-system becomes further constrained for memory, there's a chance your process will be killed.</p>
-</li>
-
-<li>{@link android.content.ComponentCallbacks2#TRIM_MEMORY_COMPLETE}
-<p>The system is running low on memory and your process is one of the first to be killed if the
-system does not recover memory now. You should release everything that's not critical to
-resuming your app state.</p>
-
-</li>
-</ul>
-
-<p>Because the {@link android.content.ComponentCallbacks2#onTrimMemory onTrimMemory()} callback was
-added in API level 14, you can use the {@link android.content.ComponentCallbacks#onLowMemory()}
-callback as a fallback for older versions, which is roughly equivalent to the {@link
-android.content.ComponentCallbacks2#TRIM_MEMORY_COMPLETE} event.</p>
-
-<p class="note"><strong>Note:</strong> When the system begins killing processes in the LRU cache,
-although it primarily works bottom-up, it does give some consideration to which processes are
-consuming more memory and will thus provide the system more memory gain if killed.
-So the less memory you consume while in the LRU list overall, the better your chances are
-to remain in the list and be able to quickly resume.</p>
-
-
-
-<h3 id="CheckHowMuchMemory">Check how much memory you should use</h3>
-
-<p>As mentioned earlier, each Android-powered device has a different amount of RAM available to the
-system and thus provides a different heap limit for each app. You can call {@link
-android.app.ActivityManager#getMemoryClass()} to get an estimate of your app's available heap in
-megabytes. If your app tries to allocate more memory than is available here, it will receive an
-{@link java.lang.OutOfMemoryError}.</p>
-
-<p>In very special situations, you can request a larger heap size by setting the <a
-href="{@docRoot}guide/topics/manifest/application-element.html#largeHeap">{@code largeHeap}</a>
-attribute to "true" in the manifest <a
-href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a>
-tag. If you do so, you can call {@link
-android.app.ActivityManager#getLargeMemoryClass()} to get an estimate of the large heap size.</p>
-
-<p>However, the ability to request a large heap is intended only for a small set of apps that can
-justify the need to consume more RAM (such as a large photo editing app). <strong>Never request a
-large heap simply because you've run out of memory</strong> and you need a quick fix&mdash;you
-should use it only when you know exactly where all your memory is being allocated and why it must
-be retained. Yet, even when you're confident your app can justify the large heap, you should avoid
-requesting it to whatever extent possible. Using the extra memory will increasingly be to the
-detriment of the overall user experience because garbage collection will take longer and system
-performance may be slower when task switching or performing other common operations.</p>
-
-<p>Additionally, the large heap size is not the same on all devices and, when running on
-devices that have limited RAM, the large heap size may be exactly the same as the regular heap
-size. So even if you do request the large heap size, you should call {@link
-android.app.ActivityManager#getMemoryClass()} to check the regular heap size and strive to always
-stay below that limit.</p>
-
-
-<h3 id="Bitmaps">Avoid wasting memory with bitmaps</h3>
-
-<p>When you load a bitmap, keep it in RAM only at the resolution you need for the current device's
-screen, scaling it down if the original bitmap is a higher resolution. Keep in mind that an
-increase in bitmap resolution results in a corresponding (increase<sup>2</sup>) in memory needed,
-because both the X and Y dimensions increase.</p>
-
-<p class="note"><strong>Note:</strong> On Android 2.3.x (API level 10) and below, bitmap objects
-always appear as the same size in your app heap regardless of the image resolution (the actual
-pixel data is stored separately in native memory). This makes it more difficult to debug the bitmap
-memory allocation because most heap analysis tools do not see the native allocation. However,
-beginning in Android 3.0 (API level 11), the bitmap pixel data is allocated in your app's Dalvik
-heap, improving garbage collection and debuggability. So if your app uses bitmaps and you're having
-trouble discovering why your app is using some memory on an older device, switch to a device
-running Android 3.0 or higher to debug it.</p>
-
-<p>For more tips about working with bitmaps, read <a
-href="{@docRoot}training/displaying-bitmaps/manage-memory.html">Managing Bitmap Memory</a>.</p>
-
-
-<h3 id="DataContainers">Use optimized data containers</h3>
-
-<p>Take advantage of optimized containers in the Android framework, such as {@link
-android.util.SparseArray}, {@link android.util.SparseBooleanArray}, and {@link
-android.support.v4.util.LongSparseArray}. The generic {@link java.util.HashMap}
-implementation can be quite memory
-inefficient because it needs a separate entry object for every mapping. Additionally, the {@link
-android.util.SparseArray} classes are more efficient because they avoid the system's need
-to <acronym title=
-"Automatic conversion from primitive types to object classes (such as int to Integer)"
->autobox</acronym>
-the key and sometimes value (which creates yet another object or two per entry). And don't be
-afraid of dropping down to raw arrays when that makes sense.</p>
-
-
-
-<h3 id="Overhead">Be aware of memory overhead</h3>
-
-<p>Be knowledgeable about the cost and overhead of the language and libraries you are using, and
-keep this information in mind when you design your app, from start to finish. Often, things on the
-surface that look innocuous may in fact have a large amount of overhead. Examples include:</p>
-<ul>
-<li>Enums often require more than twice as much memory as static constants. You should strictly
-avoid using enums on Android.</li>
-
-<li>Every class in Java (including anonymous inner classes) uses about 500 bytes of code.</li>
-
-<li>Every class instance has 12-16 bytes of RAM overhead.</li>
-
-<li>Putting a single entry into a {@link java.util.HashMap} requires the allocation of an
-additional entry object that takes 32 bytes (see the previous section about <a
-href="#DataContainers">optimized data containers</a>).</li>
-</ul>
-
-<p>A few bytes here and there quickly add up—app designs that are class- or object-heavy will suffer
-from this overhead. That can leave you in the difficult position of looking at a heap analysis and
-realizing your problem is a lot of small objects using up your RAM.</p>
-
-
-<h3 id="Abstractions">Be careful with code abstractions</h3>
-
-<p>Often, developers use abstractions simply as a "good programming practice," because abstractions
-can improve code flexibility and maintenance. However, abstractions come at a significant cost:
-generally they require a fair amount more code that needs to be executed, requiring more time and
-more RAM for that code to be mapped into memory. So if your abstractions aren't supplying a
-significant benefit, you should avoid them.</p>
-
-
-<h3 id="NanoProto">Use nano protobufs for serialized data</h3>
-
-<p><a href="https://developers.google.com/protocol-buffers/docs/overview">Protocol
-buffers</a> are a language-neutral, platform-neutral, extensible mechanism designed by Google for
-serializing structured data&mdash;think XML, but smaller, faster, and simpler. If you decide to use
-protobufs for your data, you should always use nano protobufs in your client-side code. Regular
-protobufs generate extremely verbose code, which will cause many kinds of problems in your app:
-increased RAM use, significant APK size increase, slower execution, and quickly hitting the DEX
-symbol limit.</p>
-
-<p>For more information, see the "Nano version" section in the <a
-href="https://android.googlesource.com/platform/external/protobuf/+/master/java/README.txt"
-class="external-link">protobuf readme</a>.</p>
-
-
-
-<h3 id="DependencyInjection">Avoid dependency injection frameworks</h3>
-
-<p>Using a dependency injection framework such as <a
-href="https://code.google.com/p/google-guice/" class="external-link">Guice</a> or
-<a href="https://github.com/roboguice/roboguice" class="external-link">RoboGuice</a> may be
-attractive because they can simplify the code you write and provide an adaptive environment
-that's useful for testing and other configuration changes. However, these frameworks tend to perform
-a lot of process initialization by scanning your code for annotations, which can require significant
-amounts of your code to be mapped into RAM even though you don't need it. These mapped pages are
-allocated into clean memory so Android can drop them, but that won't happen until the pages have
-been left in memory for a long period of time.</p>
-
-
-<h3 id="ExternalLibs">Be careful about using external libraries</h3>
-
-<p>External library code is often not written for mobile environments and can be inefficient when used
-for work on a mobile client. At the very least, when you decide to use an external library, you
-should assume you are taking on a significant porting and maintenance burden to optimize the
-library for mobile. Plan for that work up-front and analyze the library in terms of code size and
-RAM footprint before deciding to use it at all.</p>
-
-<p>Even libraries supposedly designed for use on Android are potentially dangerous because each
-library may do things differently. For example, one library may use nano protobufs while another
-uses micro protobufs. Now you have two different protobuf implementations in your app. This can and
-will also happen with different implementations of logging, analytics, image loading frameworks,
-caching, and all kinds of other things you don't expect. <a
-href="{@docRoot}tools/help/proguard.html">ProGuard</a> won't save you here because these
-will all be lower-level dependencies that are required by the features for which you want the
-library. This becomes especially problematic when you use an {@link android.app.Activity}
-subclass from a library (which
-will tend to have wide swaths of dependencies), when libraries use reflection (which is common and
-means you need to spend a lot of time manually tweaking ProGuard to get it to work), and so on.</p>
-
-<p>Also be careful not to fall into the trap of using a shared library for one or two features out of
-dozens of other things it does; you don't want to pull in a large amount of code and overhead that
-you don't even use. At the end of the day, if there isn't an existing implementation that is a
-strong match for what you need to do, it may be best if you create your own implementation.</p>
-
-
-<h3 id="OverallPerf">Optimize overall performance</h3>
-
-<p>A variety of information about optimizing your app's overall performance is available
-in other documents listed in <a href="{@docRoot}training/best-performance.html">Best Practices
-for Performance</a>. Many of these documents include optimizations tips for CPU performance, but
-many of these tips also help optimize your app's memory use, such as by reducing the number of
-layout objects required by your UI.</p>
-
-<p>You should also read about <a href="{@docRoot}tools/debugging/debugging-ui.html">optimizing
-your UI</a> with the layout debugging tools and take advantage of
-the optimization suggestions provided by the <a
-href="{@docRoot}tools/debugging/improving-w-lint.html">lint tool</a>.</p>
-
-
-<h3 id="Proguard">Use ProGuard to strip out any unneeded code</h3>
-
-<p>The <a href="{@docRoot}tools/help/proguard.html">ProGuard</a> tool shrinks,
-optimizes, and obfuscates your code by removing unused code and renaming classes, fields, and
-methods with semantically obscure names. Using ProGuard can make your code more compact, requiring
-fewer RAM pages to be mapped.</p>
-
-
-<h3 id="Zipalign">Use zipalign on your final APK</h3>
-
-<p>If you do any post-processing of an APK generated by a build system (including signing it
-with your final production certificate), then you must run <a
-href="{@docRoot}tools/help/zipalign.html">zipalign</a> on it to have it re-aligned.
-Failing to do so can cause your app to require significantly more RAM, because things like
-resources can no longer be mmapped from the APK.</p>
-
-<p class="note"><strong>Note:</strong> Google Play Store does not accept APK files that
-are not zipaligned.</p>
-
-
-<h3 id="AnalyzeRam">Analyze your RAM usage</h3>
-
-<p>Once you achieve a relatively stable build, begin analyzing how much RAM your app is using
-throughout all stages of its lifecycle. For information about how to analyze your app, read <a
-href="{@docRoot}tools/debugging/debugging-memory.html">Investigating Your RAM Usage</a>.</p>
-
-
-
-
-<h3 id="MultipleProcesses">Use multiple processes</h3>
-
-<p>If it's appropriate for your app, an advanced technique that may help you manage your app's
-memory is dividing components of your app into multiple processes. This technique must always be
-used carefully and <strong>most apps should not run multiple processes</strong>, as it can easily
-increase&mdash;rather than decrease&mdash;your RAM footprint if done incorrectly. It is primarily
-useful to apps that may run significant work in the background as well as the foreground and can
-manage those operations separately.</p>
-
-
-<p>An example of when multiple processes may be appropriate is when building a music player that
-plays music from a service for long period of time. If
-the entire app runs in one process, then many of the allocations performed for its activity UI must
-be kept around as long as it is playing music, even if the user is currently in another app and the
-service is controlling the playback. An app like this may be split into two process: one for its
-UI, and the other for the work that continues running in the background service.</p>
-
-<p>You can specify a separate process for each app component by declaring the <a href=
-"{@docRoot}guide/topics/manifest/service-element.html#proc">{@code android:process}</a> attribute
-for each component in the manifest file. For example, you can specify that your service should run
-in a process separate from your app's main process by declaring a new process named "background"
-(but you can name the process anything you like):</p>
-
-<pre>
-&lt;service android:name=".PlaybackService"
-         android:process=":background" />
-</pre>
-
-<p>Your process name should begin with a colon (':') to ensure that the process remains private to
-your app.</p>
-
-<p>Before you decide to create a new process, you need to understand the memory implications.
-To illustrate the consequences of each process, consider that an empty process doing basically
-nothing has an extra memory footprint of about 1.4MB, as shown by the memory information
-dump below.</p>
-
-<pre class="no-pretty-print">
-adb shell dumpsys meminfo com.example.android.apis:empty
-
-** MEMINFO in pid 10172 [com.example.android.apis:empty] **
-                Pss     Pss  Shared Private  Shared Private    Heap    Heap    Heap
-              Total   Clean   Dirty   Dirty   Clean   Clean    Size   Alloc    Free
-             ------  ------  ------  ------  ------  ------  ------  ------  ------
-  Native Heap     0       0       0       0       0       0    1864    1800      63
-  Dalvik Heap   764       0    5228     316       0       0    5584    5499      85
- Dalvik Other   619       0    3784     448       0       0
-        Stack    28       0       8      28       0       0
-    Other dev     4       0      12       0       0       4
-     .so mmap   287       0    2840     212     972       0
-    .apk mmap    54       0       0       0     136       0
-    .dex mmap   250     148       0       0    3704     148
-   Other mmap     8       0       8       8      20       0
-      Unknown   403       0     600     380       0       0
-        TOTAL  2417     148   12480    1392    4832     152    7448    7299     148
-</pre>
-
-<p class="note"><strong>Note:</strong> More information about how to read this output is provided
-in <a href="{@docRoot}tools/debugging/debugging-memory.html#ViewingAllocations">Investigating
-Your RAM Usage</a>. The key data here is the <em>Private Dirty</em> and <em>Private
-Clean</em> memory, which shows that this process is using almost 1.4MB of non-pageable RAM
-(distributed across the Dalvik heap, native allocations, book-keeping, and library-loading),
-and another 150K of RAM for code that has been mapped in to execute.</p>
-
-<p>This memory footprint for an empty process is fairly significant and it can quickly
-grow as you start doing work in that process. For
-example, here is the memory use of a process that is created only to show an activity with some
-text in it:</p>
-
-<pre class="no-pretty-print">
-** MEMINFO in pid 10226 [com.example.android.helloactivity] **
-                Pss     Pss  Shared Private  Shared Private    Heap    Heap    Heap
-              Total   Clean   Dirty   Dirty   Clean   Clean    Size   Alloc    Free
-             ------  ------  ------  ------  ------  ------  ------  ------  ------
-  Native Heap     0       0       0       0       0       0    3000    2951      48
-  Dalvik Heap  1074       0    4928     776       0       0    5744    5658      86
- Dalvik Other   802       0    3612     664       0       0
-        Stack    28       0       8      28       0       0
-       Ashmem     6       0      16       0       0       0
-    Other dev   108       0      24     104       0       4
-     .so mmap  2166       0    2824    1828    3756       0
-    .apk mmap    48       0       0       0     632       0
-    .ttf mmap     3       0       0       0      24       0
-    .dex mmap   292       4       0       0    5672       4
-   Other mmap    10       0       8       8      68       0
-      Unknown   632       0     412     624       0       0
-        TOTAL  5169       4   11832    4032   10152       8    8744    8609     134
-</pre>
-
-<p>The process has now almost tripled in size, to 4MB, simply by showing some text in the UI. This
-leads to an important conclusion: If you are going to split your app into multiple processes, only
-one process should be responsible for UI. Other processes should avoid any UI, as this will quickly
-increase the RAM required by the process (especially once you start loading bitmap assets and other
-resources). It may then be hard or impossible to reduce the memory usage once the UI is drawn.</p>
-
-<p>Additionally, when running more than one process, it's more important than ever that you keep your
-code as lean as possible, because any unnecessary RAM overhead for common implementations are now
-replicated in each process. For example, if you are using enums (though <a
-href="#Overhead">you should not use enums</a>), all of
-the RAM needed to create and initialize those constants is duplicated in each process, and any
-abstractions you have with adapters and temporaries or other overhead will likewise be replicated.</p>
-
-<p>Another concern with multiple processes is the dependencies that exist between them. For example,
-if your app has a content provider that you have running in the default process which also hosts
-your UI, then code in a background process that uses that content provider will also require that
-your UI process remain in RAM. If your goal is to have a background process that can run
-independently of a heavy-weight UI process, it can't have dependencies on content providers or
-services that execute in the UI process.</p>
-
-
-
-
-
-
-
-
-
-
-<!-- THE FOLLOWING IS OVERWHELMING AND NOT NECESSARY FOR MOST APPS, LEAVING OUT FOR NOW
-
-
-<p>You can examine the dependencies between your processes with the command:</p>
-
-<pre class="no-pretty-print">
-adb shell dumpsys activity
-</pre>
-
-<p>This dumps various information about the Activity Manager's state, ending with a list of all
-processes in their memory management order, including the reason each process is at its given
-level. For example, below is a dump with the Music app in the foreground.</p>
-
-<pre class="no-pretty-print">
-ACTIVITY MANAGER RUNNING PROCESSES (dumpsys activity processes)
-  Process LRU list (sorted by oom_adj):
-    PERS # 4: adj=sys  /F  trm= 0 20674:system/1000 (fixed)
-    PERS #39: adj=pers /F  trm= 0 20964:com.android.nfc/1027 (fixed)
-    PERS # 2: adj=pers /F  trm= 0 20959:com.android.phone/1001 (fixed)
-    PERS # 1: adj=pers /F  trm= 0 20779:com.android.systemui/u0a10057 (fixed)
-    Proc #11: adj=fore /FA trm= 0 8663:com.google.android.music:ui/u0a10043 (top-activity)
-    Proc #10: adj=fore /F  trm= 0 30881:com.google.android.music:main/u0a10043 (provider)
-        com.google.android.music/.store.MusicContentProvider<=Proc{8663:com.google.android.music:ui/u0a10043}
-    Proc # 6: adj=fore /F  trm= 0 21014:com.google.process.gapps/u0a10023 (provider)
-        com.google.android.gsf/.settings.GoogleSettingsProvider<=Proc{20935:com.google.process.location/u0a10023}
-    Proc #38: adj=vis  /F  trm= 0 21028:com.android.nfc:handover/1027 (service)
-        com.android.nfc/.handover.HandoverService<=Proc{20964:com.android.nfc/1027}
-    Proc # 7: adj=vis  /B  trm= 0 20935:com.google.process.location/u0a10023 (service)
-        com.google.android.location/.GeocodeService<=Proc{20674:system/1000}
-    Proc # 3: adj=vis  /F  trm= 0 21225:com.android.bluetooth/1002 (service)
-        com.android.bluetooth/.hfp.HeadsetService<=Proc{20674:system/1000}
-    Proc # 0: adj=vis  /F  trm= 0 20908:com.google.android.inputmethod.latin/u0a10035 (service)
-        com.google.android.inputmethod.latin/com.android.inputmethod.latin.LatinIME<=Proc{20674:system/1000}
-    Proc #34: adj=svc  /B  trm= 0 16765:com.google.android.apps.currents/u0a10012 (started-services)
-    Proc #14: adj=svc  /B  trm= 0 21148:com.google.android.gms/u0a10023 (started-services)
-    Proc #12: adj=home /B  trm= 0 20989:com.android.launcher/u0a10036 (home)
-    Proc #37: adj=svcb /B  trm= 0 15194:com.google.android.apps.googlevoice/u0a10089 (started-services)
-    Proc #17: adj=svcb /B  trm= 0 24537:android.process.media/u0a10016 (started-services)
-    Proc #35: adj=bak  /B  trm= 0 16087:com.android.defcontainer/u0a10013 (service)
-        com.android.defcontainer/.DefaultContainerService<=Proc{16050:com.android.settings/1000}
-    Proc #16: adj=bak  /B  trm= 0 7334:com.google.android.gm/u0a10022 (bg-act)
-    Proc #15: adj=bak  /B  trm= 0 22499:com.google.android.googlequicksearchbox/u0a10060 (bg-act)
-    Proc # 9: adj=bak  /B  trm= 0 20856:com.google.android.gsf.login/u0a10023 (bg-empty)
-    Proc #26: adj=bak+1/B  trm= 0 9923:com.android.mms/u0a10042 (bg-act)
-    Proc #23: adj=bak+1/B  trm= 0 16721:com.android.chrome/u0a10010 (bg-act)
-    Proc #22: adj=bak+1/B  trm= 0 17596:com.android.chrome:sandboxed_process0/u0a10010i33 (service)
-        com.android.chrome/org.chromium.content.app.SandboxedProcessService0<=Proc{16721:com.android.chrome/u0a10010}
-    Proc #19: adj=bak+1/B  trm= 0 17442:com.google.android.youtube/u0a10067 (bg-services)
-    Proc #18: adj=bak+2/B  trm= 0 16740:com.google.android.apps.plus/u0a10052 (bg-empty)
-    Proc #13: adj=bak+2/B  trm= 0 7707:com.android.musicfx/u0a10044 (bg-empty)
-    Proc #36: adj=bak+3/B  trm= 0 16050:com.android.settings/1000 (bg-act)
-    Proc #33: adj=bak+3/B  trm= 0 16863:com.android.dialer/u0a10015 (bg-act)
-</pre>
-
-
-<p class="note"><strong>Note:</strong> The exact details of what is shown here will vary across
-platform versions as process management policies are tweaked and improved.</p>
-
-
-<p>Details on the highlighted sections are:</p>
-
-<ol>
-<li>Foreground app: This is the current app running in the foreground -- it is in the "fore" memory
-class because it is the top activity on the activity stack.</li>
-
-<li>Persistent processes: These are processes that are part of the core system that must always be
-running.</li>
-
-<li>Dependent process: This shows how the Music app is using two processes. Its UI process has a
-dependency on the "main" process (through a content provider). So while the UI process is in use,
-the main process must also be kept around. This means the app's memory footprint is actually the
-sum of both processes. You will have this kind of connection on a content provider any time you
-have active calls into it or have unclosed cursors or file streams that came from it.</li>
-
-<li>Visible processes: These are processes that count in some way as "visible" to the user. This
-generally means that it is either something the user can literally see (such as a process hosting a
-paused but visible activity that is behind a non-full-screen dialog) or is something the user might
-notice if the process disappeared (such as a foreground service playing music). You should be
-certain that any process you have running at the "visible" level is indeed critical to the user,
-because they are very expensive to the overall RAM load.</li>
-
-<li>Service processes: These are processes running long-term jobs in a service. This level of the
-list is the start of less-critical processes, which the system has some freedom to kill if RAM is
-needed elsewhere. These services are still quite expensive because they can be killed only
-temporarily and the system tries to keep them running whenever possible.</li>
-
-<li>Home process: A special slot for the process that hosts the current Home activity, to try to
-prevent it from being killed as much as possible. Killing this process is much more damaging to the
-user experience than killing other cached processes, because so much user interaction goes through
-home.</li>
-
-<li>Secondary service processes: These are services that have been running for a relatively long time
-and so should be killed more aggressively when RAM is needed elsewhere.</li>
-
-<li>Cached processes: These are cached processes held in the LRU cache, which allow for fast app
-switching and component launching. These processes are not required and the system will kill them
-as needed to reclaim memory. You will often see a process hosting a running service here—this is
-part of a platform policy of allowing very long-running services to drop down into the LRU list and
-eventually be killed. If the service should continue running (as defined by the {@link
-android.app.Service#onStartCommand onStartCommand()} return value, such as {@link
-android.app.Service#START_STICKY}), the the system eventually restarts it. This avoids issues with
-such services having memory leaks that over time reduce the number of regular cached processes that
-can be kept.</li>
-
-</ol>
-
-<p>This numbered list of processes is essentially the LRU list of processes that the framework
-provides to the kernel to help it determine which processes it should kill as it needs more RAM.
-The kernel's out of memory killer will generally begin from the bottom of this list, killing the
-last process and working its way up. It may not do it in exactly this order, as it can also take
-into consideration other factors such as the relative RAM footprint of processes to some degree.</p>
-
-<p>There are many other options you can use with the activity command to analyze further details of
-your app's state&mdash;use <code>adb shell dumpsys activity -h</code> for help on its use.</p>
-
--->
diff --git a/docs/html/training/articles/perf-tips.jd b/docs/html/training/articles/perf-tips.jd
index 82de69a..30cab14 100644
--- a/docs/html/training/articles/perf-tips.jd
+++ b/docs/html/training/articles/perf-tips.jd
@@ -28,7 +28,8 @@
 performance effects. Choosing the right algorithms and data structures should always be your
 priority, but is outside the scope of this document. You should use the tips in this document
 as general coding practices that you can incorporate into your habits for general code
-efficiency.</p>
+efficiency.
+</p>
 
 <p>There are two basic rules for writing efficient code:</p>
 <ul>
@@ -49,8 +50,7 @@
 without.</p>
 
 <p>To ensure your app performs well across a wide variety of devices, ensure
-your code is efficient at all levels and agressively optimize your performance.</p>
-
+your code is efficient at all levels and aggressively optimize your performance.</p>
 
 <h2 id="ObjectCreation">Avoid Creating Unnecessary Objects</h2>
 
diff --git a/docs/html/training/auto/audio/index.jd b/docs/html/training/auto/audio/index.jd
index 3a1b1e88..6588367 100644
--- a/docs/html/training/auto/audio/index.jd
+++ b/docs/html/training/auto/audio/index.jd
@@ -596,7 +596,7 @@
 </a>
 <h2 id="support_voice">Support Voice Actions</h2>
 
-<p>To reduce driver distractions, you can add voice actions in your audio playback app. With voice
+<p>To reduce driver distractions, you must add voice actions in your audio playback app. With voice
 action support, users can launch your app and play audio by providing voice input on Auto screens.
 If your audio playback app is already active and the user says
 <i>“Play a song”</i>, the system starts playing music without requiring the user to look at or touch
diff --git a/docs/html/training/backup/autosyncapi.jd b/docs/html/training/backup/autosyncapi.jd
deleted file mode 100644
index e0df7bb..0000000
--- a/docs/html/training/backup/autosyncapi.jd
+++ /dev/null
@@ -1,370 +0,0 @@
-page.title=Configuring Auto Backup for Apps
-page.tags=backup, marshmallow, androidm
-page.keywords=backup, autobackup
-page.image=images/cards/card-auto-backup_2x.png
-
-@jd:body
-
-<div id="tb-wrapper">
-<div id="tb">
-<h2>This lesson teaches you to</h2>
-<ol>
-        <li><a href="#configuring">Configure Data Backup</a></li>
-        <li><a href="#previous-androids">Support Lower Versions of Android</a></li>
-        <li><a href="#testing">Test Backup Configuration</a></li>
-        <li><a href="#issues">Handle Google Cloud Messaging</a></li>
-</ol>
-    <h2>You should also read</h2>
-    <ul>
-      <li><a href="{@docRoot}guide/topics/data/backup.html">Data Backup</a></li>
-      <li><a href="{@docRoot}training/backup/backupapi.html">Using the Backup API</a>
-      </li>
-    </ul>
-
-</div>
-</div>
-
-<p>
-  Users frequently invest time and effort to configure apps just the way they like them. Switching
-  to a new device can cancel out all that careful configuration. For apps whose <a href=
-  "{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">target SDK version</a>
-  is Android 6.0 (API level 23) and higher, devices running Android 6.0 and higher automatically
-  back up app data to the cloud. The system performs this automatic backup
-  for nearly all app data by default, and does so without your having to write any additional app
-  code.
-</p>
-
-<p class="note">
-<strong>Note:</strong> To protect user privacy, the device user must have opted in to Google
-services for Auto Backup to work. The Google services opt-in dialog appears when the user goes
-through the Setup Wizard or configures the first Google account on the device.
-</p>
-
-<p>
-  When a user installs your app on
-  a new device, or reinstalls your app on one (for example, after a factory reset), the system
-  automatically restores the app data from the cloud. This lesson provides information about how to
-  configure the Auto Backup for Apps feature, explaining its default behavior and how to
-  exclude data that you don't want the system to back up.
-</p>
-
-<p>
-  The automatic backup feature preserves the data your app creates on a user device by uploading it
-  to the user’s Google Drive account and encrypting it. There is no charge to you or the user for
-  data storage, and the saved data does not count towards the user's personal Google Drive quota.
-  Each app can store up to 25MB. Once its backed-up data reaches 25MB, the app no longer sends
-  data to the cloud. If the system performs a data restore, it uses the last data snapshot that
-  the app had sent to the cloud.
-</p>
-
-<p>Automatic backups occur when the following conditions are met:</p>
-  <ul>
-     <li>The device is idle.</li>
-     <li>The device is charging.</li>
-     <li>The device is connected to a Wi-Fi network.</li>
-     <li>At least 24 hours have elapsed since the last backup.</li>
-  </ul>
-</p>
-
-<h2 id="configuring">Configure Data Backup</h2>
-
-<p>
-  On devices running Android 6.0 (API level 23) or higher, the default system behavior is to back up
-  almost all data that an app creates. The exception is <a href="#auto-exclude">
-  automatically excluded data files</a>. This section explains how you can use settings in
-  your app <a href="{@docRoot}guide/topics/manifest/manifest-intro.html">manifest</a> to further
-  limit and configure what data the system backs up.
-</p>
-
-<h3 id="include-exclude">Including or excluding data</h3>
-
-<p>
-  Depending on what data your app needs and how you save it, you may need to set specific
-  rules for including or excluding certain files or directories. Auto Backup for Apps
-  lets you set these backup rules through the app manifest, in which you specify a backup scheme
-  configuration XML file. For example:
-</p>
-
-<pre>
-&lt;?xml version="1.0" encoding="utf-8"?&gt;
-&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
-        package="com.my.appexample"&gt;
-    &lt;uses-sdk android:minSdkVersion="23"/&gt;
-    &lt;uses-sdk android:targetSdkVersion="23"/&gt;
-    &lt;application ...
-<strong>        android:fullBackupContent="&#64;xml/mybackupscheme"&gt;</strong>
-    &lt;/app&gt;
-    ...
-&lt;/manifest&gt;
-</pre>
-
-<p>
-  In this example, the <code>android:fullBackupContent</code> attribute specifies an XML file
-  called {@code mybackupscheme.xml}, which resides in the <code>res/xml/</code> directory of your
-  app development project. This configuration file contains rules controlling which files are backed
-  up. The following example code shows a configuration file that excludes a specific file,
-  {@code device_info.db}:
-</p>
-
-<pre>
-&lt;?xml version="1.0" encoding="utf-8"?&gt;
-&lt;full-backup-content&gt;
-    &lt;exclude domain="database" path="device_info.db"/&gt;
-&lt;/full-backup-content&gt;
-</pre>
-
-<h3 id="auto-exclude">Automatically excluded data files</h3>
-
-<p>
-  Most apps do not need to, and in fact should not, back up all data. For example, the system
-  should not back up temporary files and caches. For this reason, the automatic backup
-  service excludes certain data files by default:
-</p>
-
-<ul>
-  <li>Files in the directories to which the
-  {@link android.content.Context#getCacheDir getCacheDir()} and
-  {@link android.content.Context#getCodeCacheDir getCodeCacheDir()} methods refer.
-  </li>
-
-  <li>Files located on external storage, unless they reside in the directory to which the
-    {@link android.content.Context#getExternalFilesDir getExternalFilesDir()} method refers.
-  </li>
-
-  <li>Files located in the directory to which the
-    {@link android.content.Context#getNoBackupFilesDir getNoBackupFilesDir()} method refers.
-  </li>
-</ul>
-<h3>Backup Configuration Syntax</h3>
-
-<p>
-  The backup service configuration allows you to specify what files to include or exclude from
-  backup. The syntax for the data backup configuration XML file is as follows:
-</p>
-
-<pre>
-&lt;full-backup-content&gt;
-    &lt;include domain=["file" | "database" | "sharedpref" | "external" | "root"]
-    path="string" /&gt;
-    &lt;exclude domain=["file" | "database" | "sharedpref" | "external" | "root"]
-    path="string" /&gt;
-&lt;/full-backup-content&gt;
-</pre>
-
-<p>
-  The following elements and attributes allow you to specify the files to include in, and exclude
-  from, backup:
-</p>
-
-<ul>
-  <li>
-  <code>&lt;include&gt;</code>: Specifies a set of resources to
-  back up, instead of having the system back up all data in your app by default. If you specify
-  an <code>&lt;include&gt;</code> element, the system backs up <em>only the resources specified</em>
-  with this element. You can specify multiple sets of resources to back up by using multiple
-  <code>&lt;include&gt;</code> elements
-  </li>
-
-  <li>
-  <code>&lt;exclude&gt;</code>: Specifies any data you want the system to exclude
-  when it does a full backup. If you target the same set of resources with both the
-  <code>&lt;include&gt;</code> and <code>&lt;exclude&gt;</code> elements,
-  <code>&lt;exclude&gt;</code> takes precedence.
-  </li>
-
-  <li>
-  <code>domain</code>: Specifies the type of resource you want to include in,
-  or exclude from, backup. Valid values for this attribute include:
-
-
-
-  <ul>
-    <li>
-    <code>root</code>: Specifies that the resource is in the app’s root directory.
-    </li>
-
-    <li>
-    <code>file</code>: Specifies a resource in the directory returned by the
-    {@link android.content.Context#getFilesDir getFilesDir()} method.
-    </li>
-
-    <li>
-    <code>database</code>: Specifies a database that the
-    {@link android.content.Context#getDatabasePath getDatabasePath()} method returns, or that
-    the app interacts with via the {@link android.database.sqlite.SQLiteOpenHelper} class.
-    </li>
-
-    <li>
-    <code>sharedpref</code>: Specifies a {@link android.content.SharedPreferences} object
-    that the {@link android.content.Context#getSharedPreferences getSharedPreferences()}
-    method returns.
-    </li>
-
-    <li>
-    <code>external</code>: Specifies that the resource is in external storage, and corresponds
-    to a file in the directory that the
-    {@link android.content.Context#getExternalFilesDir getExternalFilesDir()} method returns.
-    </li>
-  </ul>
-  </li>
-    <li>
-    <code>path</code>: Specifies the file path to a resource that you want to include in, or
-    exclude from, backup.
-    </li>
-
-  </li>
-</ul>
-
-
-<h3 id="disabling">Disabling data backups</h3>
-
-<p>
-  You can choose to prevent automatic backups of any of your app data by setting the
-  <code>android:allowBackup</code> attribute to <code>false</code> in the {@code app} element of
-  your manifest. This setting is illustrated in the following example:
-</p>
-
-<pre>
-&lt;?xml version="1.0" encoding="utf-8"?&gt;
-&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        xmlns:tools="http://schemas.android.com/tools"
-        package="com.my.appexample"&gt;
-    &lt;uses-sdk android:minSdkVersion="23"/&gt;
-    &lt;uses-sdk android:targetSdkVersion="23"/&gt;
-    &lt;application ...
-<strong>        android:allowBackup="false"&gt;</strong>
-    &lt;/application&gt;
-    ...
-&lt;/manifest&gt;
-</pre>
-
-<h2 id="previous-androids">Support Lower Versions of Android</h2>
-
-<p>There are two scenarios in which you may also need to support versions of Android lower
-than 6.0 (API level 23): You may be updating your existing app to take advantage of the
-new auto backup functionality in Android 6.0, while wanting
-to continue supporting earlier versions of Android. Or you may be releasing a new app, but
-want to make sure devices running on versions of Android predating 6.0 also have backup
-functionality.</p>
-
-<h3 id="updating">Updating an existing app to support auto backup</h3>
-
-<p>Earlier versions of Android supported a key/value-pair-based backup mechanism, in which the app
-defines a subclass of {@link android.app.backup.BackupAgent} and sets
-<a href="{@docRoot}guide/topics/manifest/application-element.html#agent">
-{@code android:backupAgent}</a> in its
-<a href="{@docRoot}guide/topics/manifest/application-element.html">app manifest</a>. If your app
-used this legacy approach, you can transition to full-data backups by adding the
-{@code android:fullBackupOnly="true"} attribute to the
-<a href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application/>}</a>
-element in the manifest. When running on a device with Android 5.1
-(API level 22) or lower, your app ignores this value in the manifest, and continues performing
-backups in the previous manner.</p>
-
-<p>Even if you’re not using key/value backups, you can still use the approach described above to do
-any custom processing in {@link android.app.Activity#onCreate(android.os.Bundle) onCreate()}
-or {@link android.app.backup.BackupAgent#onFullBackup onFullBackup()}. You can also use that
-approach to receive a notification when a restore operation happens in
-{@link android.app.backup.BackupAgent#onRestoreFinished onRestoreFinished()}. If you want to retain
-the system's default implementation of
-<a href="#include-exclude">XML include/exclude rules handling</a>, call
-{@link android.app.backup.BackupAgent#onFullBackup super.onFullBackup()}.</p>
-
-<h3 id="lower-versions">Giving your new app support for lower versions of Android</h3>
-
-<p>If you are creating a new app that targets Android 6.0, but you also want to enable cloud backup
-for devices running on Android 5.1 (API level 22) and lower, you must also
-<a href="{@docRoot}training/backup/backupapi.html">implement the Backup API</a>.</p>
-
-<h2 id="testing">Test Backup Configuration</h2>
-
-<p>
-  Once you have created a backup configuration, you should test it to make sure your app saves data
-  and can restore it properly.
-</p>
-
-
-<h3>Enabling Backup Logging</h3>
-
-<p>
-  To help determine how the backup feature is parsing your XML file, enable logging before
-  performing a test backup:
-</p>
-
-<pre class="no-pretty-print">
-$ adb shell setprop log.tag.BackupXmlParserLogging VERBOSE
-</pre>
-
-<h3>Testing Backup</h3>
-
-<p>To manually run a backup, first initialize the Backup Manager by executing the following
-  command:
-</p>
-
-<pre class="no-pretty-print">
-$ adb shell bmgr run
-</pre>
-
-<p>
-  Next, manually back up your application using the following command. Use the
-  <code>&lt;PACKAGE&gt;</code> parameter to specify the package name for your app:
-</p>
-
-<pre class="no-pretty-print">
-$ adb shell bmgr fullbackup &lt;PACKAGE&gt;</pre>
-
-
-<h3>Testing restore</h3>
-
-<p>
-  To manually initiate a restore after the system has backed up your app data, execute the following
-  command, using the <code>&lt;PACKAGE&gt;</code> parameter to specify the package name for your
-  app:
-</p>
-
-<pre class="noprettyprint">
-$ adb shell bmgr restore &lt;PACKAGE&gt;
-</pre>
-
-<p class="warning">
-  <b>Warning:</b> This action stops your app and wipes its data before performing the restore
-  operation.
-</p>
-
-<p>
-  You can test automatic restore for your app by uninstalling and reinstalling your app. The app
-  data is automatically restored from the cloud once the app installation is complete.
-</p>
-
-
-<h3>Troubleshooting backups</h3>
-
-<p>
-  If backup fails, you can clear the backup data and associated metadata either by turning backup
-  off and on in <strong>Settings &gt; Backup</strong>, factory-resetting the device, or
-  executing this command:
-</p>
-
-<pre>$ adb shell bmgr wipe &lt;TRANSPORT&gt; &lt;PACKAGE&gt;</pre>
-
-<p>
-  You must prepend <code>com.google.android.gms</code> to the {@code <TRANSPORT>} value.
-  To get the list of <a href="{@docRoot}google/backup/index.html">transports</a>, execute the
-  following command:
-</p>
-
-<pre>$ adb shell bmgr list transports</pre>
-
-<h2 id="gcm">Handle Google Cloud Messaging</h2>
-
-  <p>
-  For apps that use <a href="https://developers.google.com/cloud-messaging/gcm">Google Cloud
-  Messaging</a> (GCM) for push notifications, backing up the registration
-  token that Google Cloud Messaging registration returned can cause unexpected behavior in
-  notifications for the restored app. This is because when a user installs your app on a new device,
-  the app must <a href="https://developers.google.com/cloud-messaging/android/client#sample-register">
-  query the GCM API for a new registration token</a>. If the old registration is present, because the
-  system had backed it up and restored it, the app doesn't seek the new token. To prevent this issue
-  from arising, exclude the registration token from the set of backed-up files.
-  </p>
diff --git a/docs/html/training/backup/backupapi.jd b/docs/html/training/backup/backupapi.jd
deleted file mode 100644
index 2f3e939..0000000
--- a/docs/html/training/backup/backupapi.jd
+++ /dev/null
@@ -1,200 +0,0 @@
-page.title=Using the Backup API
-parent.title=Backing up App Data to the Cloud
-parent.link=index.html
-
-trainingnavtop=true
-
-next.title=Making the Most of Google Cloud Messaging
-next.link=gcm.html
-
-@jd:body
-
-<div id="tb-wrapper">
-  <div id="tb">
-    <h2>This lesson teaches you to</h2>
-    <ol>
-      <li><a href="#register">Register for the Android Backup Service</a></li>
-      <li><a href="#manifest">Configure Your Manifest</a></li>
-      <li><a href="#agent">Write Your Backup Agent</a></li>
-      <li><a href="#backup">Request a Backup</a></li>
-      <li><a href="#restore">Restore from a Backup</a></li>
-    </ol>
-    <h2>You should also read</h2>
-    <ul>
-      <li><a href="{@docRoot}guide/topics/data/backup.html">Data Backup</a></li>
-      <li><a href="{@docRoot}training/backup/autosyncapi.html">Configuring Auto Backup for Apps</a>
-      (Android 6.0 (API level 23) and higher)</li>
-    </ul>
-  </div>
-</div>
-
-<p>When a user purchases a new device or resets their existing one, they might
-expect that when Google Play restores your app back to their device during the
-initial setup, the previous data associated with the app restores as well. On versions of Android
-prior to 6.0 (API level 23), app data is not restored by default, and all the user's accomplishments
-or settings in your app are lost.</p>
-<p>For situations where the volume of data is relatively light (less than a
-megabyte), like the user's preferences, notes, game high scores or other
-stats, the Backup API provides a lightweight solution.  This lesson walks you
-through integrating the Backup API into your application, and restoring data to
-new devices using the Backup API.
-
-<p class="note">
-<strong>Note:</strong> Devices running Android 6.0 and higher
-<a href="{@docRoot}training/backup/autosyncapi.html">automatically back up</a>
-nearly all data by default.
-</p>
-
-<h2 id="register">Register for the Android Backup Service</h2>
-<p>This lesson requires the use of the <a
-  href="{@docRoot}google/backup/index.html">Android Backup
-  Service</a>, which requires registration.  Go ahead and <a
-  href="http://code.google.com/android/backup/signup.html">register here</a>.  Once
-that's done, the service pre-populates an XML tag for insertion in your Android
-Manifest, which looks like this:</p>
-<pre>
-&lt;meta-data android:name="com.google.android.backup.api_key"
-android:value="ABcDe1FGHij2KlmN3oPQRs4TUvW5xYZ" /&gt;
-</pre>
-<p>Note that each backup key works with a specific package name.  If you have
-different applications, register separate keys for each one.</p>
-
-
-<h2 id="manifest">Configure Your Manifest</h2>
-<p>Use of the Android Backup Service requires two additions to your application
-manifest.  First, declare the name of the class that acts as your backup agent,
-then add the snippet above as a child element of the Application tag.  Assuming
-your backup agent is going to be called {@code TheBackupAgent}, here's an example of
-what the manifest looks like with this tag included:</p>
-
-<pre>
-&lt;application android:label="MyApp"
-             android:backupAgent="TheBackupAgent"&gt;
-    ...
-    &lt;meta-data android:name="com.google.android.backup.api_key"
-    android:value="ABcDe1FGHij2KlmN3oPQRs4TUvW5xYZ" /&gt;
-    ...
-&lt;/application&gt;
-</pre>
-<h2 id="agent">Write Your Backup Agent</h2>
-<p>The easiest way to create your backup agent is by extending the wrapper class
-{@link android.app.backup.BackupAgentHelper}.  Creating this helper class is
-actually a very simple process.  Just create a class with the same name as you
-used in the manifest in the previous step (in this example, {@code
-TheBackupAgent}),
-and extend {@code BackupAgentHelper}.  Then override the {@link
-android.app.backup.BackupAgent#onCreate()}.</p>
-
-<p>Inside the {@link android.app.backup.BackupAgent#onCreate()} method, create a {@link
-android.app.backup.BackupHelper}.  These helpers are
-specialized classes for backing up certain kinds of data.  The Android framework
-currently includes two such helpers:  {@link
-android.app.backup.FileBackupHelper} and {@link
-android.app.backup.SharedPreferencesBackupHelper}.  After you create the helper
-and point it at the data you want to back up, just add it to the
-BackupAgentHelper using the {@link android.app.backup.BackupAgentHelper#addHelper(String, BackupHelper) addHelper()}
-method, adding a key which is used to
-retrieve the data later.  In most cases the entire
-implementation is perhaps 10 lines of code.</p>
-
-<p>Here's an example that backs up a high scores file.</p>
-
-<pre>
-import android.app.backup.BackupAgentHelper;
-import android.app.backup.FileBackupHelper;
-
-
-public class TheBackupAgent extends BackupAgentHelper {
-  // The name of the SharedPreferences file
-  static final String HIGH_SCORES_FILENAME = "scores";
-
-  // A key to uniquely identify the set of backup data
-  static final String FILES_BACKUP_KEY = "myfiles";
-
-  // Allocate a helper and add it to the backup agent
-  &#64;Override
-  void onCreate() {
-      FileBackupHelper helper = new FileBackupHelper(this, HIGH_SCORES_FILENAME);
-      addHelper(FILES_BACKUP_KEY, helper);
-  }
-}
-</pre>
-<p>For added flexibility, {@link android.app.backup.FileBackupHelper}'s
-constructor can take a variable number of filenames.  You could just as easily
-have backed up both a high scores file and a game progress file just by adding
-an extra parameter, like this:</p>
-<pre>
-&#64;Override
-  void onCreate() {
-      FileBackupHelper helper = new FileBackupHelper(this, HIGH_SCORES_FILENAME, PROGRESS_FILENAME);
-      addHelper(FILES_BACKUP_KEY, helper);
-  }
-</pre>
-<p>Backing up preferences is similarly easy.  Create a {@link
-android.app.backup.SharedPreferencesBackupHelper}  the same way you did a {@link
-android.app.backup.FileBackupHelper}.  In this case, instead of adding filenames
-to the constructor, add the names of the shared preference groups being used by
-your application.  Here's an example of how your backup agent helper might look if
-high scores are implemented as preferences instead of a flat file:</p>
-
-<pre>
-import android.app.backup.BackupAgentHelper;
-import android.app.backup.SharedPreferencesBackupHelper;
-
-public class TheBackupAgent extends BackupAgentHelper {
-    // The names of the SharedPreferences groups that the application maintains.  These
-    // are the same strings that are passed to getSharedPreferences(String, int).
-    static final String PREFS_DISPLAY = "displayprefs";
-    static final String PREFS_SCORES = "highscores";
-
-    // An arbitrary string used within the BackupAgentHelper implementation to
-    // identify the SharedPreferencesBackupHelper's data.
-    static final String MY_PREFS_BACKUP_KEY = "myprefs";
-
-    // Simply allocate a helper and install it
-    void onCreate() {
-       SharedPreferencesBackupHelper helper =
-                new SharedPreferencesBackupHelper(this, PREFS_DISPLAY, PREFS_SCORES);
-        addHelper(MY_PREFS_BACKUP_KEY, helper);
-    }
-}
-</pre>
-
-<p>You can add as many backup helper instances to your backup agent helper as you
-like, but remember that you only need one of each type.  One {@link
-android.app.backup.FileBackupHelper} handles all the files that you need to back up, and one
-{@link android.app.backup.SharedPreferencesBackupHelper} handles all the shared
-preferencegroups you need backed up.
-</p>
-
-
-<h2 id="backup">Request a Backup</h2>
-<p>In order to request a backup, just create an instance of the {@link
-android.app.backup.BackupManager}, and call it's {@link
-android.app.backup.BackupManager#dataChanged()} method.</p>
-
-<pre>
-import android.app.backup.BackupManager;
-...
-
-public void requestBackup() {
-  BackupManager bm = new BackupManager(this);
-  bm.dataChanged();
-}
-</pre>
-
-<p>This call notifies the backup manager that there is data ready to be backed
-up to the cloud.  At some point in the future, the backup manager then calls
-your backup agent's {@link
-android.app.backup.BackupAgent#onBackup(ParcelFileDescriptor, BackupDataOutput,
-ParcelFileDescriptor) onBackup()} method.  You can make
-the call whenever your data has changed, without having to worry about causing
-excessive network activity.  If you request a backup twice before a backup
-occurs, the backup only occurs once.</p>
-
-
-<h2 id="restore">Restore from a Backup</h2>
-<p>Typically you shouldn't ever have to manually request a restore, as it
-happens automatically when your application is installed on a device.  However,
-if it <em>is</em> necessary to trigger a manual restore, just call the
-{@link android.app.backup.BackupManager#requestRestore(RestoreObserver) requestRestore()} method.</p>
diff --git a/docs/html/training/backup/index.jd b/docs/html/training/backup/index.jd
deleted file mode 100644
index 4449fde..0000000
--- a/docs/html/training/backup/index.jd
+++ /dev/null
@@ -1,47 +0,0 @@
-page.title=Backing up App Data to the Cloud
-page.tags=cloud,sync,backup
-
-trainingnavtop=true
-startpage=true
-
-@jd:body
-
-<div id="tb-wrapper">
-<div id="tb">
-
-<h2>Dependencies and prerequisites</h2>
-<ul>
-  <li>Android 2.2 (API level 8) and higher</li>
-</ul>
-</div>
-</div>
-
-<p>Users often invest significant time and effort creating data and setting
-preferences within apps. Preserving that data for users if they replace a broken
-device or upgrade to a new one is an important part of ensuring a great user
-experience.</p>
-
-<p>This class covers techniques for backing up data to the cloud so that
-users can restore their data when recovering from a data loss (such as a factory
-reset) or installing your application on a new device.</p>
-
-<p>It is important to note that the API for cloud backup changed with the
-release of Android 6.0 (API level 23). For your app to support backup both
-on devices running Android 6.0, and those running Android 5.1 (API level
-22) and lower, you must implement both techniques that this class explains.</p>
-
-<h2>Lessons</h2>
-
-<dl>
-	<dt><strong><a href="autosyncapi.html">Configuring Auto Backup for Apps</a></strong></dt>
-	<dd>This lesson applies to Android 6.0 (API level 23) and higher. Learn how to accomplish
-  seamless app data backup and restore with zero additional lines of application code.</dd>
-</dl>
-
-<dl>
-  <dt><strong><a href="backupapi.html">Using the Backup API</a></strong></dt>
-  <dd>This lesson applies to Android 5.1 (API level 22) and lower. Learn how to integrate the Backup
-  API into your Android app, so all of that  app's user data, such as preferences, notes, and high
-  scores, updates seamlessly across all devices linked to that Google account.</dd>
-</dl>
-
diff --git a/docs/html/training/best-performance.jd b/docs/html/training/best-performance.jd
index 8ea6fd5..bb88e99 100644
--- a/docs/html/training/best-performance.jd
+++ b/docs/html/training/best-performance.jd
@@ -5,4 +5,9 @@
 
 
 <p>These classes and articles help you build an app that's smooth, responsive,
-and uses as little battery as possible.</p>
\ No newline at end of file
+and uses as little battery as possible.</p>
+
+<p>Along with this section, you can find additional information about optimizing
+your app in the <a href="/topic/performance/index.html">Performance and
+Power</a> section.</p>
+
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index d0dccba..d2bf881 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -649,25 +649,7 @@
           </li>
         </ul>
       </li>
-
       <li class="nav-section">
-        <div class="nav-section-header">
-          <a href="<?cs var:toroot ?>training/backup/index.html"
-             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."
-            >Backing up App Data to the Cloud</a>
-        </div>
-        <ul>
-          <li><a href="<?cs var:toroot ?>training/backup/autosyncapi.html">
-            Configuring Auto Backup
-          </a>
-          </li>
-          <li><a href="<?cs var:toroot ?>training/backup/backupapi.html">
-            Using the Backup API
-          </a>
-          </li>
-        </ul>
         <li><a href="<?cs var:toroot ?>training/cloudsave/conflict-res.html"
            description=
            "How to design a robust conflict resolution strategy for apps that save data to the cloud."
@@ -1888,6 +1870,12 @@
           >Managing Your App's Memory</a>
       </li>
       <li>
+        <a href="<?cs var:toroot ?>training/articles/memory-overview.html"
+          description=
+          "How Android manages app process and memory allocation."
+          >Overview of Android Memory Management</a>
+      </li>
+      <li>
         <a href="<?cs var:toroot ?>training/articles/perf-tips.html"
            description=
            "How to optimize your app's performance in various ways to improve its
diff --git a/docs/html/wear/preview/downloads.jd b/docs/html/wear/preview/downloads.jd
index 08ed233..da6a06d 100644
--- a/docs/html/wear/preview/downloads.jd
+++ b/docs/html/wear/preview/downloads.jd
@@ -626,7 +626,7 @@
       the accounts on the phone.
       </li>
 
-      <li>Choose a Google account to add and sync to your watch.
+      <li>Choose a Google Account to add and sync to your watch.
       </li>
 
       <li>Confirm the screen lock and enter the password to start the copying of
@@ -647,8 +647,19 @@
     </h2>
 
     <p>
-      To test with the Android Emulator, create a virtual device in Android
-      Studio as follows:
+      To test with the Android Emulator,
+      confirm that you have the latest version of the <strong>Android SDK
+      Platform-tools</strong> from the <a href=
+      "{@docRoot}studio/intro/update.html#sdk-manager">SDK Manager</a>.
+    </p>
+
+    <p>
+      After you create a virtual device as described below, follow the steps for
+      <a href="#set_up_a_phone">setting up a phone</a> with the beta version of
+      the Android Wear companion app.
+    </p>
+
+    <p>Create a new virtual device in Android Studio as follows:
     </p>
 
     <ol>
@@ -659,8 +670,8 @@
       <li>Click <strong>Create Virtual Device</strong>.
       </li>
 
-      <li>In the <strong>Category</strong> pane, select Wear and
-       choose a hardware profile.
+      <li>In the <strong>Category</strong> pane, select <strong>Wear</strong>
+       and choose a hardware profile.
        The Android Wear 2.0 Developer Preview
        is only optimized for round devices currently, so we recommend not
        using the square or chin profiles for now.
@@ -679,16 +690,68 @@
       <li>Verify the configuration of the Android Virtual Device (AVD) and
       click <strong>Finish</strong>.
       </li>
+
+      <li>Start the emulator by selecting the new virtual device, clicking the
+      <strong>Play</strong> button, and waiting until
+      the emulator initializes and shows the Android Wear home screen.
+      </li>
     </ol>
 
     <p>
-      You can now test an application with a virtual preview device
+      Pair the phone with the emulator, and sync a Google Account, as follows:
+    </p>
+
+    <ol>
+      <li>Follow the steps for
+      <a href="#set_up_a_phone">setting up a phone</a> with the beta version of
+      the Android Wear companion app.
+      </li>
+
+      <li>On the phone, enable Developer Options and USB Debugging.
+      </li>
+
+      <li>Connect the phone to your computer through USB.
+      </li>
+
+      <li>Forward the AVD's communication port to the connected handheld device
+      (each time the phone is connected):<br>
+      <code>adb -d forward tcp:5601 tcp:5601</code>
+      </li>
+
+      <li>On the phone, in the Android Wear app, begin the standard pairing
+      process. For example, on the Welcome screen, tap the
+      <strong>Set It Up</strong> button.
+      Alternatively, if an existing watch already is paired, in the upper-left
+      drop-down, tap <strong>Add a New Watch</strong>.
+      </li>
+
+      <li>On the phone, in the Android Wear app, tap the
+      Overflow button, and then tap
+      <strong>Pair with Emulator</strong>.
+      </li>
+
+      <li>Tap the Settings icon.
+      </li>
+
+      <li>Under Device Settings, tap <strong>Emulator</strong>.
+      </li>
+
+      <li>Tap <strong>Accounts</strong> and select a Google Account,
+      and follow the steps in the wizard to
+      sync the account with the emulator. If necessary, type the screen-lock
+      device password, and Google Account password, to start the account sync.
+      </li>
+    </ol>
+
+    <p>
+      You can now test an app with a virtual preview device
       in the <a href=
       "{@docRoot}tools/devices/emulator.html">Android Emulator</a>. For more
       information about using virtual devices, see <a href=
-      "{@docRoot}tools/devices/managing-avds.html">Managing AVDs with the AVD
-      Manager</a>.
+      "{@docRoot}studio/run/managing-avds.html">
+      Create and Manage Virtual Devices</a>.
     </p>
+
  </div><!-- landing -->
 
 </div><!-- relative wrapper -->
diff --git a/docs/html/wear/preview/features/app-distribution.jd b/docs/html/wear/preview/features/app-distribution.jd
index 319efa6..afc9516 100644
--- a/docs/html/wear/preview/features/app-distribution.jd
+++ b/docs/html/wear/preview/features/app-distribution.jd
@@ -132,6 +132,9 @@
 
 <pre>
 android {
+    // Allows you to reference product flavors in your
+    // phone module's build.gradle file
+    publishNonDefault true
     ...
     defaultConfig
     {
@@ -148,6 +151,7 @@
             minSdkVersion 24
         }
     }
+}
 </pre>
 
     <p>
@@ -158,7 +162,7 @@
 <pre>
 dependencies {
     ...
-    wearApp project(path: ':wearable', configuration: 'wear1Release')
+    wearApp project(path: ':wear', configuration: 'wear1Release')
 }
 </pre>
 
diff --git a/docs/html/wear/preview/support.jd b/docs/html/wear/preview/support.jd
index 7636d86..6006627 100644
--- a/docs/html/wear/preview/support.jd
+++ b/docs/html/wear/preview/support.jd
@@ -319,6 +319,22 @@
       </li>
     </ul>
 
+    <h4 id="account">
+      Account sync
+    </h4>
+
+    <ul>
+      <li>Account sync initiated from watch settings may not work reliably.
+      Instead, add accounts from the setup flow of the Android Wear app, or using
+      the Accounts settings for a device from the Android Wear app.
+      </li>
+
+      <li>The list of accounts that can be synced is the same as the list of accounts
+      on the phone. So to add a new account, use the Android settings on the phone,
+      and then proceed to Android Wear app to sync that account.
+      </li>
+    </ul>
+
     <h4 id="devices">
       Devices
     </h4>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 49721cf..7ce750d 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -48,11 +48,6 @@
     // pixel data.
     private static final long NATIVE_ALLOCATION_SIZE = 32;
 
-    /**
-     * Backing buffer for the Bitmap.
-     */
-    private byte[] mBuffer;
-
     // Convenience for JNI access
     private final long mNativePtr;
 
@@ -108,7 +103,7 @@
      * int (pointer).
      */
     // called from JNI
-    Bitmap(long nativeBitmap, byte[] buffer, int width, int height, int density,
+    Bitmap(long nativeBitmap, int width, int height, int density,
             boolean isMutable, boolean requestPremultiplied,
             byte[] ninePatchChunk, NinePatch.InsetStruct ninePatchInsets) {
         if (nativeBitmap == 0) {
@@ -119,7 +114,6 @@
         mHeight = height;
         mIsMutable = isMutable;
         mRequestPremultiplied = requestPremultiplied;
-        mBuffer = buffer;
 
         mNinePatchChunk = ninePatchChunk;
         mNinePatchInsets = ninePatchInsets;
@@ -128,10 +122,7 @@
         }
 
         mNativePtr = nativeBitmap;
-        long nativeSize = NATIVE_ALLOCATION_SIZE;
-        if (buffer == null) {
-            nativeSize += getByteCount();
-        }
+        long nativeSize = NATIVE_ALLOCATION_SIZE + getAllocationByteCount();
         NativeAllocationRegistry registry = new NativeAllocationRegistry(
             Bitmap.class.getClassLoader(), nativeGetNativeFinalizer(), nativeSize);
         registry.registerNativeAllocation(this, nativeBitmap);
@@ -256,12 +247,8 @@
         if (!isMutable()) {
             throw new IllegalStateException("only mutable bitmaps may be reconfigured");
         }
-        if (mBuffer == null) {
-            throw new IllegalStateException("native-backed bitmaps may not be reconfigured");
-        }
 
-        nativeReconfigure(mNativePtr, width, height, config.nativeInt,
-                mBuffer.length, mRequestPremultiplied);
+        nativeReconfigure(mNativePtr, width, height, config.nativeInt, mRequestPremultiplied);
         mWidth = width;
         mHeight = height;
     }
@@ -343,7 +330,6 @@
                 // false indicates that it is still in use at the native level and these
                 // objects should not be collected now. They will be collected later when the
                 // Bitmap itself is collected.
-                mBuffer = null;
                 mNinePatchChunk = null;
             }
             mRecycled = true;
@@ -1273,12 +1259,7 @@
      * @see #reconfigure(int, int, Config)
      */
     public final int getAllocationByteCount() {
-        if (mBuffer == null) {
-            // native backed bitmaps don't support reconfiguration,
-            // so alloc size is always content size
-            return getByteCount();
-        }
-        return mBuffer.length;
+        return nativeGetAllocationByteCount(mNativePtr);
     }
 
     /**
@@ -1695,8 +1676,7 @@
     private static native long nativeGetNativeFinalizer();
     private static native boolean nativeRecycle(long nativeBitmap);
     private static native void nativeReconfigure(long nativeBitmap, int width, int height,
-                                                 int config, int allocSize,
-                                                 boolean isPremultiplied);
+                                                 int config, boolean isPremultiplied);
 
     private static native boolean nativeCompress(long nativeBitmap, int format,
                                             int quality, OutputStream stream,
@@ -1742,4 +1722,5 @@
     private static native boolean nativeSameAs(long nativeBitmap0, long nativeBitmap1);
     private static native long nativeRefPixelRef(long nativeBitmap);
     private static native void nativePrepareToDraw(long nativeBitmap);
+    private static native int nativeGetAllocationByteCount(long nativeBitmap);
 }
diff --git a/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java
index 1e8f11b..35cedae 100644
--- a/graphics/java/android/graphics/Matrix.java
+++ b/graphics/java/android/graphics/Matrix.java
@@ -16,8 +16,12 @@
 
 package android.graphics;
 
-import java.io.PrintWriter;
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
 
+import libcore.util.NativeAllocationRegistry;
+
+import java.io.PrintWriter;
 
 /**
  * The Matrix class holds a 3x3 matrix for transforming coordinates.
@@ -216,352 +220,345 @@
         }
     };
 
+    // sizeof(SkMatrix) is 9 * sizeof(float) + uint32_t
+    private static final long NATIVE_ALLOCATION_SIZE = 40;
+
+    private static class NoImagePreloadHolder {
+        public static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
+                Matrix.class.getClassLoader(), nGetNativeFinalizer(), NATIVE_ALLOCATION_SIZE);
+    }
+
     /**
      * @hide
      */
-    public long native_instance;
+    public final long native_instance;
 
     /**
      * Create an identity matrix
      */
     public Matrix() {
-        native_instance = native_create(0);
+        native_instance = nCreate(0);
+        NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, native_instance);
     }
 
     /**
      * Create a matrix that is a (deep) copy of src
+     *
      * @param src The matrix to copy into this matrix
      */
     public Matrix(Matrix src) {
-        native_instance = native_create(src != null ? src.native_instance : 0);
+        native_instance = nCreate(src != null ? src.native_instance : 0);
+        NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, native_instance);
     }
 
     /**
-     * Returns true if the matrix is identity.
-     * This maybe faster than testing if (getType() == 0)
+     * Returns true if the matrix is identity. This maybe faster than testing if (getType() == 0)
      */
     public boolean isIdentity() {
-        return native_isIdentity(native_instance);
+        return nIsIdentity(native_instance);
     }
 
     /**
-     * Gets whether this matrix is affine. An affine matrix preserves
-     * straight lines and has no perspective.
+     * Gets whether this matrix is affine. An affine matrix preserves straight lines and has no
+     * perspective.
      *
      * @return Whether the matrix is affine.
      */
     public boolean isAffine() {
-        return native_isAffine(native_instance);
+        return nIsAffine(native_instance);
     }
 
     /**
-     * Returns true if will map a rectangle to another rectangle. This can be
-     * true if the matrix is identity, scale-only, or rotates a multiple of 90
-     * degrees.
+     * Returns true if will map a rectangle to another rectangle. This can be true if the matrix is
+     * identity, scale-only, or rotates a multiple of 90 degrees.
      */
     public boolean rectStaysRect() {
-        return native_rectStaysRect(native_instance);
+        return nRectStaysRect(native_instance);
     }
 
     /**
-     * (deep) copy the src matrix into this matrix. If src is null, reset this
-     * matrix to the identity matrix.
+     * (deep) copy the src matrix into this matrix. If src is null, reset this matrix to the
+     * identity matrix.
      */
     public void set(Matrix src) {
         if (src == null) {
             reset();
         } else {
-            native_set(native_instance, src.native_instance);
+            nSet(native_instance, src.native_instance);
         }
     }
 
-    /** Returns true iff obj is a Matrix and its values equal our values.
-    */
+    /**
+     * Returns true iff obj is a Matrix and its values equal our values.
+     */
     @Override
     public boolean equals(Object obj) {
-        //if (obj == this) return true;     -- NaN value would mean matrix != itself
-        if (!(obj instanceof Matrix)) return false;
-        return native_equals(native_instance, ((Matrix)obj).native_instance);
+        // if (obj == this) return true; -- NaN value would mean matrix != itself
+        if (!(obj instanceof Matrix)) {
+            return false;
+        }
+        return nEquals(native_instance, ((Matrix) obj).native_instance);
     }
 
     @Override
     public int hashCode() {
         // This should generate the hash code by performing some arithmetic operation on all
         // the matrix elements -- our equals() does an element-by-element comparison, and we
-        // need to ensure that the hash code for two equal objects is the same.  We're not
+        // need to ensure that the hash code for two equal objects is the same. We're not
         // really using this at the moment, so we take the easy way out.
         return 44;
     }
 
     /** Set the matrix to identity */
     public void reset() {
-        native_reset(native_instance);
+        nReset(native_instance);
     }
 
     /** Set the matrix to translate by (dx, dy). */
     public void setTranslate(float dx, float dy) {
-        native_setTranslate(native_instance, dx, dy);
+        nSetTranslate(native_instance, dx, dy);
     }
 
     /**
-     * Set the matrix to scale by sx and sy, with a pivot point at (px, py).
-     * The pivot point is the coordinate that should remain unchanged by the
-     * specified transformation.
+     * Set the matrix to scale by sx and sy, with a pivot point at (px, py). The pivot point is the
+     * coordinate that should remain unchanged by the specified transformation.
      */
     public void setScale(float sx, float sy, float px, float py) {
-        native_setScale(native_instance, sx, sy, px, py);
+        nSetScale(native_instance, sx, sy, px, py);
     }
 
     /** Set the matrix to scale by sx and sy. */
     public void setScale(float sx, float sy) {
-        native_setScale(native_instance, sx, sy);
+        nSetScale(native_instance, sx, sy);
     }
 
     /**
-     * Set the matrix to rotate by the specified number of degrees, with a pivot
-     * point at (px, py). The pivot point is the coordinate that should remain
-     * unchanged by the specified transformation.
+     * Set the matrix to rotate by the specified number of degrees, with a pivot point at (px, py).
+     * The pivot point is the coordinate that should remain unchanged by the specified
+     * transformation.
      */
     public void setRotate(float degrees, float px, float py) {
-        native_setRotate(native_instance, degrees, px, py);
+        nSetRotate(native_instance, degrees, px, py);
     }
 
     /**
      * Set the matrix to rotate about (0,0) by the specified number of degrees.
      */
     public void setRotate(float degrees) {
-        native_setRotate(native_instance, degrees);
+        nSetRotate(native_instance, degrees);
     }
 
     /**
-     * Set the matrix to rotate by the specified sine and cosine values, with a
-     * pivot point at (px, py). The pivot point is the coordinate that should
-     * remain unchanged by the specified transformation.
+     * Set the matrix to rotate by the specified sine and cosine values, with a pivot point at (px,
+     * py). The pivot point is the coordinate that should remain unchanged by the specified
+     * transformation.
      */
     public void setSinCos(float sinValue, float cosValue, float px, float py) {
-        native_setSinCos(native_instance, sinValue, cosValue, px, py);
+        nSetSinCos(native_instance, sinValue, cosValue, px, py);
     }
 
     /** Set the matrix to rotate by the specified sine and cosine values. */
     public void setSinCos(float sinValue, float cosValue) {
-        native_setSinCos(native_instance, sinValue, cosValue);
+        nSetSinCos(native_instance, sinValue, cosValue);
     }
 
     /**
-     * Set the matrix to skew by sx and sy, with a pivot point at (px, py).
-     * The pivot point is the coordinate that should remain unchanged by the
-     * specified transformation.
+     * Set the matrix to skew by sx and sy, with a pivot point at (px, py). The pivot point is the
+     * coordinate that should remain unchanged by the specified transformation.
      */
     public void setSkew(float kx, float ky, float px, float py) {
-        native_setSkew(native_instance, kx, ky, px, py);
+        nSetSkew(native_instance, kx, ky, px, py);
     }
 
     /** Set the matrix to skew by sx and sy. */
     public void setSkew(float kx, float ky) {
-        native_setSkew(native_instance, kx, ky);
+        nSetSkew(native_instance, kx, ky);
     }
 
     /**
-     * Set the matrix to the concatenation of the two specified matrices and
-     * return true.
-     *
-     * <p>Either of the two matrices may also be the target matrix, that is
-     * <code>matrixA.setConcat(matrixA, matrixB);</code> is valid.</p>
-     *
-     * <p class="note">In {@link android.os.Build.VERSION_CODES#GINGERBREAD_MR1} and below, this
-     * function returns true only if the result can be represented. In
-     * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and above, it always returns true.</p>
+     * Set the matrix to the concatenation of the two specified matrices and return true.
+     * <p>
+     * Either of the two matrices may also be the target matrix, that is
+     * <code>matrixA.setConcat(matrixA, matrixB);</code> is valid.
+     * </p>
+     * <p class="note">
+     * In {@link android.os.Build.VERSION_CODES#GINGERBREAD_MR1} and below, this function returns
+     * true only if the result can be represented. In
+     * {@link android.os.Build.VERSION_CODES#HONEYCOMB} and above, it always returns true.
+     * </p>
      */
     public boolean setConcat(Matrix a, Matrix b) {
-        native_setConcat(native_instance, a.native_instance, b.native_instance);
+        nSetConcat(native_instance, a.native_instance, b.native_instance);
         return true;
     }
 
     /**
-     * Preconcats the matrix with the specified translation.
-     * M' = M * T(dx, dy)
+     * Preconcats the matrix with the specified translation. M' = M * T(dx, dy)
      */
     public boolean preTranslate(float dx, float dy) {
-        native_preTranslate(native_instance, dx, dy);
+        nPreTranslate(native_instance, dx, dy);
         return true;
     }
 
     /**
-     * Preconcats the matrix with the specified scale.
-     * M' = M * S(sx, sy, px, py)
+     * Preconcats the matrix with the specified scale. M' = M * S(sx, sy, px, py)
      */
     public boolean preScale(float sx, float sy, float px, float py) {
-        native_preScale(native_instance, sx, sy, px, py);
+        nPreScale(native_instance, sx, sy, px, py);
         return true;
     }
 
     /**
-     * Preconcats the matrix with the specified scale.
-     * M' = M * S(sx, sy)
+     * Preconcats the matrix with the specified scale. M' = M * S(sx, sy)
      */
     public boolean preScale(float sx, float sy) {
-        native_preScale(native_instance, sx, sy);
+        nPreScale(native_instance, sx, sy);
         return true;
     }
 
     /**
-     * Preconcats the matrix with the specified rotation.
-     * M' = M * R(degrees, px, py)
+     * Preconcats the matrix with the specified rotation. M' = M * R(degrees, px, py)
      */
     public boolean preRotate(float degrees, float px, float py) {
-        native_preRotate(native_instance, degrees, px, py);
+        nPreRotate(native_instance, degrees, px, py);
         return true;
     }
 
     /**
-     * Preconcats the matrix with the specified rotation.
-     * M' = M * R(degrees)
+     * Preconcats the matrix with the specified rotation. M' = M * R(degrees)
      */
     public boolean preRotate(float degrees) {
-        native_preRotate(native_instance, degrees);
+        nPreRotate(native_instance, degrees);
         return true;
     }
 
     /**
-     * Preconcats the matrix with the specified skew.
-     * M' = M * K(kx, ky, px, py)
+     * Preconcats the matrix with the specified skew. M' = M * K(kx, ky, px, py)
      */
     public boolean preSkew(float kx, float ky, float px, float py) {
-        native_preSkew(native_instance, kx, ky, px, py);
+        nPreSkew(native_instance, kx, ky, px, py);
         return true;
     }
 
     /**
-     * Preconcats the matrix with the specified skew.
-     * M' = M * K(kx, ky)
+     * Preconcats the matrix with the specified skew. M' = M * K(kx, ky)
      */
     public boolean preSkew(float kx, float ky) {
-        native_preSkew(native_instance, kx, ky);
+        nPreSkew(native_instance, kx, ky);
         return true;
     }
 
     /**
-     * Preconcats the matrix with the specified matrix.
-     * M' = M * other
+     * Preconcats the matrix with the specified matrix. M' = M * other
      */
     public boolean preConcat(Matrix other) {
-        native_preConcat(native_instance, other.native_instance);
+        nPreConcat(native_instance, other.native_instance);
         return true;
     }
 
     /**
-     * Postconcats the matrix with the specified translation.
-     * M' = T(dx, dy) * M
+     * Postconcats the matrix with the specified translation. M' = T(dx, dy) * M
      */
     public boolean postTranslate(float dx, float dy) {
-        native_postTranslate(native_instance, dx, dy);
+        nPostTranslate(native_instance, dx, dy);
         return true;
     }
 
     /**
-     * Postconcats the matrix with the specified scale.
-     * M' = S(sx, sy, px, py) * M
+     * Postconcats the matrix with the specified scale. M' = S(sx, sy, px, py) * M
      */
     public boolean postScale(float sx, float sy, float px, float py) {
-        native_postScale(native_instance, sx, sy, px, py);
+        nPostScale(native_instance, sx, sy, px, py);
         return true;
     }
 
     /**
-     * Postconcats the matrix with the specified scale.
-     * M' = S(sx, sy) * M
+     * Postconcats the matrix with the specified scale. M' = S(sx, sy) * M
      */
     public boolean postScale(float sx, float sy) {
-        native_postScale(native_instance, sx, sy);
+        nPostScale(native_instance, sx, sy);
         return true;
     }
 
     /**
-     * Postconcats the matrix with the specified rotation.
-     * M' = R(degrees, px, py) * M
+     * Postconcats the matrix with the specified rotation. M' = R(degrees, px, py) * M
      */
     public boolean postRotate(float degrees, float px, float py) {
-        native_postRotate(native_instance, degrees, px, py);
+        nPostRotate(native_instance, degrees, px, py);
         return true;
     }
 
     /**
-     * Postconcats the matrix with the specified rotation.
-     * M' = R(degrees) * M
+     * Postconcats the matrix with the specified rotation. M' = R(degrees) * M
      */
     public boolean postRotate(float degrees) {
-        native_postRotate(native_instance, degrees);
+        nPostRotate(native_instance, degrees);
         return true;
     }
 
     /**
-     * Postconcats the matrix with the specified skew.
-     * M' = K(kx, ky, px, py) * M
+     * Postconcats the matrix with the specified skew. M' = K(kx, ky, px, py) * M
      */
     public boolean postSkew(float kx, float ky, float px, float py) {
-        native_postSkew(native_instance, kx, ky, px, py);
+        nPostSkew(native_instance, kx, ky, px, py);
         return true;
     }
 
     /**
-     * Postconcats the matrix with the specified skew.
-     * M' = K(kx, ky) * M
+     * Postconcats the matrix with the specified skew. M' = K(kx, ky) * M
      */
     public boolean postSkew(float kx, float ky) {
-        native_postSkew(native_instance, kx, ky);
+        nPostSkew(native_instance, kx, ky);
         return true;
     }
 
     /**
-     * Postconcats the matrix with the specified matrix.
-     * M' = other * M
+     * Postconcats the matrix with the specified matrix. M' = other * M
      */
     public boolean postConcat(Matrix other) {
-        native_postConcat(native_instance, other.native_instance);
+        nPostConcat(native_instance, other.native_instance);
         return true;
     }
 
-    /** Controlls how the src rect should align into the dst rect for
-        setRectToRect().
-    */
+    /**
+     * Controlls how the src rect should align into the dst rect for setRectToRect().
+     */
     public enum ScaleToFit {
         /**
-         * Scale in X and Y independently, so that src matches dst exactly.
-         * This may change the aspect ratio of the src.
+         * Scale in X and Y independently, so that src matches dst exactly. This may change the
+         * aspect ratio of the src.
          */
-        FILL    (0),
+        FILL(0),
         /**
-         * Compute a scale that will maintain the original src aspect ratio,
-         * but will also ensure that src fits entirely inside dst. At least one
-         * axis (X or Y) will fit exactly. START aligns the result to the
-         * left and top edges of dst.
+         * Compute a scale that will maintain the original src aspect ratio, but will also ensure
+         * that src fits entirely inside dst. At least one axis (X or Y) will fit exactly. START
+         * aligns the result to the left and top edges of dst.
          */
-        START   (1),
+        START(1),
         /**
-         * Compute a scale that will maintain the original src aspect ratio,
-         * but will also ensure that src fits entirely inside dst. At least one
-         * axis (X or Y) will fit exactly. The result is centered inside dst.
+         * Compute a scale that will maintain the original src aspect ratio, but will also ensure
+         * that src fits entirely inside dst. At least one axis (X or Y) will fit exactly. The
+         * result is centered inside dst.
          */
-        CENTER  (2),
+        CENTER(2),
         /**
-         * Compute a scale that will maintain the original src aspect ratio,
-         * but will also ensure that src fits entirely inside dst. At least one
-         * axis (X or Y) will fit exactly. END aligns the result to the
-         * right and bottom edges of dst.
+         * Compute a scale that will maintain the original src aspect ratio, but will also ensure
+         * that src fits entirely inside dst. At least one axis (X or Y) will fit exactly. END
+         * aligns the result to the right and bottom edges of dst.
          */
-        END     (3);
+        END(3);
 
         // the native values must match those in SkMatrix.h
         ScaleToFit(int nativeInt) {
             this.nativeInt = nativeInt;
         }
+
         final int nativeInt;
     }
 
     /**
-     * Set the matrix to the scale and translate values that map the source
-     * rectangle to the destination rectangle, returning true if the the result
-     * can be represented.
+     * Set the matrix to the scale and translate values that map the source rectangle to the
+     * destination rectangle, returning true if the the result can be represented.
      *
      * @param src the source rectangle to map from.
      * @param dst the destination rectangle to map to.
@@ -572,13 +569,13 @@
         if (dst == null || src == null) {
             throw new NullPointerException();
         }
-        return native_setRectToRect(native_instance, src, dst, stf.nativeInt);
+        return nSetRectToRect(native_instance, src, dst, stf.nativeInt);
     }
 
     // private helper to perform range checks on arrays of "points"
     private static void checkPointArrays(float[] src, int srcIndex,
-                                         float[] dst, int dstIndex,
-                                         int pointCount) {
+            float[] dst, int dstIndex,
+            int pointCount) {
         // check for too-small and too-big indices
         int srcStop = srcIndex + (pointCount << 1);
         int dstStop = dstIndex + (pointCount << 1);
@@ -589,84 +586,81 @@
     }
 
     /**
-     * Set the matrix such that the specified src points would map to the
-     * specified dst points. The "points" are represented as an array of floats,
-     * order [x0, y0, x1, y1, ...], where each "point" is 2 float values.
+     * Set the matrix such that the specified src points would map to the specified dst points. The
+     * "points" are represented as an array of floats, order [x0, y0, x1, y1, ...], where each
+     * "point" is 2 float values.
      *
-     * @param src   The array of src [x,y] pairs (points)
+     * @param src The array of src [x,y] pairs (points)
      * @param srcIndex Index of the first pair of src values
-     * @param dst   The array of dst [x,y] pairs (points)
+     * @param dst The array of dst [x,y] pairs (points)
      * @param dstIndex Index of the first pair of dst values
      * @param pointCount The number of pairs/points to be used. Must be [0..4]
      * @return true if the matrix was set to the specified transformation
      */
     public boolean setPolyToPoly(float[] src, int srcIndex,
-                                 float[] dst, int dstIndex,
-                                 int pointCount) {
+            float[] dst, int dstIndex,
+            int pointCount) {
         if (pointCount > 4) {
             throw new IllegalArgumentException();
         }
         checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
-        return native_setPolyToPoly(native_instance, src, srcIndex,
-                                    dst, dstIndex, pointCount);
+        return nSetPolyToPoly(native_instance, src, srcIndex,
+                dst, dstIndex, pointCount);
     }
 
     /**
-     * If this matrix can be inverted, return true and if inverse is not null,
-     * set inverse to be the inverse of this matrix. If this matrix cannot be
-     * inverted, ignore inverse and return false.
+     * If this matrix can be inverted, return true and if inverse is not null, set inverse to be the
+     * inverse of this matrix. If this matrix cannot be inverted, ignore inverse and return false.
      */
     public boolean invert(Matrix inverse) {
-        return native_invert(native_instance, inverse.native_instance);
+        return nInvert(native_instance, inverse.native_instance);
     }
 
     /**
-    * Apply this matrix to the array of 2D points specified by src, and write
-     * the transformed points into the array of points specified by dst. The
-     * two arrays represent their "points" as pairs of floats [x, y].
+     * Apply this matrix to the array of 2D points specified by src, and write the transformed
+     * points into the array of points specified by dst. The two arrays represent their "points" as
+     * pairs of floats [x, y].
      *
-     * @param dst   The array of dst points (x,y pairs)
+     * @param dst The array of dst points (x,y pairs)
      * @param dstIndex The index of the first [x,y] pair of dst floats
-     * @param src   The array of src points (x,y pairs)
+     * @param src The array of src points (x,y pairs)
      * @param srcIndex The index of the first [x,y] pair of src floats
      * @param pointCount The number of points (x,y pairs) to transform
      */
     public void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,
-                          int pointCount) {
+            int pointCount) {
         checkPointArrays(src, srcIndex, dst, dstIndex, pointCount);
-        native_mapPoints(native_instance, dst, dstIndex, src, srcIndex,
-                         pointCount, true);
+        nMapPoints(native_instance, dst, dstIndex, src, srcIndex,
+                pointCount, true);
     }
 
     /**
-    * Apply this matrix to the array of 2D vectors specified by src, and write
-     * the transformed vectors into the array of vectors specified by dst. The
-     * two arrays represent their "vectors" as pairs of floats [x, y].
+     * Apply this matrix to the array of 2D vectors specified by src, and write the transformed
+     * vectors into the array of vectors specified by dst. The two arrays represent their "vectors"
+     * as pairs of floats [x, y]. Note: this method does not apply the translation associated with
+     * the matrix. Use {@link Matrix#mapPoints(float[], int, float[], int, int)} if you want the
+     * translation to be applied.
      *
-     * Note: this method does not apply the translation associated with the matrix. Use
-     * {@link Matrix#mapPoints(float[], int, float[], int, int)} if you want the translation
-     * to be applied.
-     *
-     * @param dst   The array of dst vectors (x,y pairs)
+     * @param dst The array of dst vectors (x,y pairs)
      * @param dstIndex The index of the first [x,y] pair of dst floats
-     * @param src   The array of src vectors (x,y pairs)
+     * @param src The array of src vectors (x,y pairs)
      * @param srcIndex The index of the first [x,y] pair of src floats
      * @param vectorCount The number of vectors (x,y pairs) to transform
      */
     public void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex,
-                          int vectorCount) {
+            int vectorCount) {
         checkPointArrays(src, srcIndex, dst, dstIndex, vectorCount);
-        native_mapPoints(native_instance, dst, dstIndex, src, srcIndex,
-                         vectorCount, false);
+        nMapPoints(native_instance, dst, dstIndex, src, srcIndex,
+                vectorCount, false);
     }
 
     /**
-     * Apply this matrix to the array of 2D points specified by src, and write
-     * the transformed points into the array of points specified by dst. The
-     * two arrays represent their "points" as pairs of floats [x, y].
+     * Apply this matrix to the array of 2D points specified by src, and write the transformed
+     * points into the array of points specified by dst. The two arrays represent their "points" as
+     * pairs of floats [x, y].
      *
-     * @param dst   The array of dst points (x,y pairs)
-     * @param src   The array of src points (x,y pairs)
+     * @param dst The array of dst points (x,y pairs)
+     * @param src The array of src points (x,y pairs)
      */
     public void mapPoints(float[] dst, float[] src) {
         if (dst.length != src.length) {
@@ -676,15 +670,14 @@
     }
 
     /**
-     * Apply this matrix to the array of 2D vectors specified by src, and write
-     * the transformed vectors into the array of vectors specified by dst. The
-     * two arrays represent their "vectors" as pairs of floats [x, y].
+     * Apply this matrix to the array of 2D vectors specified by src, and write the transformed
+     * vectors into the array of vectors specified by dst. The two arrays represent their "vectors"
+     * as pairs of floats [x, y]. Note: this method does not apply the translation associated with
+     * the matrix. Use {@link Matrix#mapPoints(float[], float[])} if you want the translation to be
+     * applied.
      *
-     * Note: this method does not apply the translation associated with the matrix. Use
-     * {@link Matrix#mapPoints(float[], float[])} if you want the translation to be applied.
-     *
-     * @param dst   The array of dst vectors (x,y pairs)
-     * @param src   The array of src vectors (x,y pairs)
+     * @param dst The array of dst vectors (x,y pairs)
+     * @param src The array of src vectors (x,y pairs)
      */
     public void mapVectors(float[] dst, float[] src) {
         if (dst.length != src.length) {
@@ -694,8 +687,8 @@
     }
 
     /**
-     * Apply this matrix to the array of 2D points, and write the transformed
-     * points back into the array
+     * Apply this matrix to the array of 2D points, and write the transformed points back into the
+     * array
      *
      * @param pts The array [x0, y0, x1, y1, ...] of points to transform.
      */
@@ -704,10 +697,8 @@
     }
 
     /**
-     * Apply this matrix to the array of 2D vectors, and write the transformed
-     * vectors back into the array.
-     *
-     * Note: this method does not apply the translation associated with the matrix. Use
+     * Apply this matrix to the array of 2D vectors, and write the transformed vectors back into the
+     * array. Note: this method does not apply the translation associated with the matrix. Use
      * {@link Matrix#mapPoints(float[])} if you want the translation to be applied.
      *
      * @param vecs The array [x0, y0, x1, y1, ...] of vectors to transform.
@@ -717,9 +708,9 @@
     }
 
     /**
-     * Apply this matrix to the src rectangle, and write the transformed
-     * rectangle into dst. This is accomplished by transforming the 4 corners of
-     * src, and then setting dst to the bounds of those points.
+     * Apply this matrix to the src rectangle, and write the transformed rectangle into dst. This is
+     * accomplished by transforming the 4 corners of src, and then setting dst to the bounds of
+     * those points.
      *
      * @param dst Where the transformed rectangle is written.
      * @param src The original rectangle to be transformed.
@@ -729,13 +720,13 @@
         if (dst == null || src == null) {
             throw new NullPointerException();
         }
-        return native_mapRect(native_instance, dst, src);
+        return nMapRect(native_instance, dst, src);
     }
 
     /**
-     * Apply this matrix to the rectangle, and write the transformed rectangle
-     * back into it. This is accomplished by transforming the 4 corners of rect,
-     * and then setting it to the bounds of those points
+     * Apply this matrix to the rectangle, and write the transformed rectangle back into it. This is
+     * accomplished by transforming the 4 corners of rect, and then setting it to the bounds of
+     * those points
      *
      * @param rect The rectangle to transform.
      * @return the result of calling rectStaysRect()
@@ -745,34 +736,33 @@
     }
 
     /**
-     * Return the mean radius of a circle after it has been mapped by
-     * this matrix. NOTE: in perspective this value assumes the circle
-     * has its center at the origin.
+     * Return the mean radius of a circle after it has been mapped by this matrix. NOTE: in
+     * perspective this value assumes the circle has its center at the origin.
      */
     public float mapRadius(float radius) {
-        return native_mapRadius(native_instance, radius);
+        return nMapRadius(native_instance, radius);
     }
 
-    /** Copy 9 values from the matrix into the array.
-    */
+    /**
+     * Copy 9 values from the matrix into the array.
+     */
     public void getValues(float[] values) {
         if (values.length < 9) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        native_getValues(native_instance, values);
+        nGetValues(native_instance, values);
     }
 
-    /** Copy 9 values from the array into the matrix.
-        Depending on the implementation of Matrix, these may be
-        transformed into 16.16 integers in the Matrix, such that
-        a subsequent call to getValues() will not yield exactly
-        the same values.
-    */
+    /**
+     * Copy 9 values from the array into the matrix. Depending on the implementation of Matrix,
+     * these may be transformed into 16.16 integers in the Matrix, such that a subsequent call to
+     * getValues() will not yield exactly the same values.
+     */
     public void setValues(float[] values) {
         if (values.length < 9) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        native_setValues(native_instance, values);
+        nSetValues(native_instance, values);
     }
 
     @Override
@@ -798,122 +788,155 @@
         float[] values = new float[9];
         getValues(values);
         sb.append('[');
-        sb.append(values[0]); sb.append(", "); sb.append(values[1]); sb.append(", ");
-        sb.append(values[2]); sb.append("][");
-        sb.append(values[3]); sb.append(", "); sb.append(values[4]); sb.append(", ");
-        sb.append(values[5]); sb.append("][");
-        sb.append(values[6]); sb.append(", "); sb.append(values[7]); sb.append(", ");
-        sb.append(values[8]); sb.append(']');
+        sb.append(values[0]);
+        sb.append(", ");
+        sb.append(values[1]);
+        sb.append(", ");
+        sb.append(values[2]);
+        sb.append("][");
+        sb.append(values[3]);
+        sb.append(", ");
+        sb.append(values[4]);
+        sb.append(", ");
+        sb.append(values[5]);
+        sb.append("][");
+        sb.append(values[6]);
+        sb.append(", ");
+        sb.append(values[7]);
+        sb.append(", ");
+        sb.append(values[8]);
+        sb.append(']');
     }
 
     /**
      * Print short string, to optimize dumping.
+     *
      * @hide
      */
     public void printShortString(PrintWriter pw) {
         float[] values = new float[9];
         getValues(values);
         pw.print('[');
-        pw.print(values[0]); pw.print(", "); pw.print(values[1]); pw.print(", ");
-                pw.print(values[2]); pw.print("][");
-        pw.print(values[3]); pw.print(", "); pw.print(values[4]); pw.print(", ");
-                pw.print(values[5]); pw.print("][");
-        pw.print(values[6]); pw.print(", "); pw.print(values[7]); pw.print(", ");
-                pw.print(values[8]); pw.print(']');
+        pw.print(values[0]);
+        pw.print(", ");
+        pw.print(values[1]);
+        pw.print(", ");
+        pw.print(values[2]);
+        pw.print("][");
+        pw.print(values[3]);
+        pw.print(", ");
+        pw.print(values[4]);
+        pw.print(", ");
+        pw.print(values[5]);
+        pw.print("][");
+        pw.print(values[6]);
+        pw.print(", ");
+        pw.print(values[7]);
+        pw.print(", ");
+        pw.print(values[8]);
+        pw.print(']');
 
     }
 
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            finalizer(native_instance);
-            native_instance = 0;  // Other finalizers can still call us.
-        } finally {
-            super.finalize();
-        }
-    }
-
-    /*package*/ final long ni() {
+    /* package */ final long ni() {
         return native_instance;
     }
 
-    private static native long native_create(long native_src_or_zero);
-    private static native boolean native_isIdentity(long native_object);
-    private static native boolean native_isAffine(long native_object);
-    private static native boolean native_rectStaysRect(long native_object);
-    private static native void native_reset(long native_object);
-    private static native void native_set(long native_object,
-                                          long native_other);
-    private static native void native_setTranslate(long native_object,
-                                                   float dx, float dy);
-    private static native void native_setScale(long native_object,
-                                        float sx, float sy, float px, float py);
-    private static native void native_setScale(long native_object,
-                                               float sx, float sy);
-    private static native void native_setRotate(long native_object,
-                                            float degrees, float px, float py);
-    private static native void native_setRotate(long native_object,
-                                                float degrees);
-    private static native void native_setSinCos(long native_object,
-                            float sinValue, float cosValue, float px, float py);
-    private static native void native_setSinCos(long native_object,
-                                                float sinValue, float cosValue);
-    private static native void native_setSkew(long native_object,
-                                        float kx, float ky, float px, float py);
-    private static native void native_setSkew(long native_object,
-                                              float kx, float ky);
-    private static native void native_setConcat(long native_object,
-                                                long native_a,
-                                                long native_b);
-    private static native void native_preTranslate(long native_object,
-                                                   float dx, float dy);
-    private static native void native_preScale(long native_object,
-                                               float sx, float sy, float px, float py);
-    private static native void native_preScale(long native_object,
-                                               float sx, float sy);
-    private static native void native_preRotate(long native_object,
-                                                float degrees, float px, float py);
-    private static native void native_preRotate(long native_object,
-                                                float degrees);
-    private static native void native_preSkew(long native_object,
-                                              float kx, float ky, float px, float py);
-    private static native void native_preSkew(long native_object,
-                                              float kx, float ky);
-    private static native void native_preConcat(long native_object,
-                                                long native_other_matrix);
-    private static native void native_postTranslate(long native_object,
-                                                    float dx, float dy);
-    private static native void native_postScale(long native_object,
-                                                float sx, float sy, float px, float py);
-    private static native void native_postScale(long native_object,
-                                                float sx, float sy);
-    private static native void native_postRotate(long native_object,
-                                                 float degrees, float px, float py);
-    private static native void native_postRotate(long native_object,
-                                                 float degrees);
-    private static native void native_postSkew(long native_object,
-                                               float kx, float ky, float px, float py);
-    private static native void native_postSkew(long native_object,
-                                               float kx, float ky);
-    private static native void native_postConcat(long native_object,
-                                                 long native_other_matrix);
-    private static native boolean native_setRectToRect(long native_object,
-                                                RectF src, RectF dst, int stf);
-    private static native boolean native_setPolyToPoly(long native_object,
-        float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount);
-    private static native boolean native_invert(long native_object,
-                                                long native_inverse);
-    private static native void native_mapPoints(long native_object,
-                        float[] dst, int dstIndex, float[] src, int srcIndex,
-                        int ptCount, boolean isPts);
-    private static native boolean native_mapRect(long native_object,
-                                                 RectF dst, RectF src);
-    private static native float native_mapRadius(long native_object,
-                                                 float radius);
-    private static native void native_getValues(long native_object,
-                                                float[] values);
-    private static native void native_setValues(long native_object,
-                                                float[] values);
-    private static native boolean native_equals(long native_a, long native_b);
-    private static native void finalizer(long native_instance);
+    // ------------------ Regular JNI ------------------------
+
+    private static native long nCreate(long nSrc_or_zero);
+    private static native long nGetNativeFinalizer();
+
+
+    // ------------------ Fast JNI ------------------------
+
+    @FastNative
+    private static native boolean nSetRectToRect(long nObject,
+            RectF src, RectF dst, int stf);
+    @FastNative
+    private static native boolean nSetPolyToPoly(long nObject,
+            float[] src, int srcIndex, float[] dst, int dstIndex, int pointCount);
+    @FastNative
+    private static native void nMapPoints(long nObject,
+            float[] dst, int dstIndex, float[] src, int srcIndex,
+            int ptCount, boolean isPts);
+    @FastNative
+    private static native boolean nMapRect(long nObject, RectF dst, RectF src);
+    @FastNative
+    private static native void nGetValues(long nObject, float[] values);
+    @FastNative
+    private static native void nSetValues(long nObject, float[] values);
+
+
+    // ------------------ Critical JNI ------------------------
+
+    @CriticalNative
+    private static native boolean nIsIdentity(long nObject);
+    @CriticalNative
+    private static native boolean nIsAffine(long nObject);
+    @CriticalNative
+    private static native boolean nRectStaysRect(long nObject);
+    @CriticalNative
+    private static native void nReset(long nObject);
+    @CriticalNative
+    private static native void nSet(long nObject, long nOther);
+    @CriticalNative
+    private static native void nSetTranslate(long nObject, float dx, float dy);
+    @CriticalNative
+    private static native void nSetScale(long nObject, float sx, float sy, float px, float py);
+    @CriticalNative
+    private static native void nSetScale(long nObject, float sx, float sy);
+    @CriticalNative
+    private static native void nSetRotate(long nObject, float degrees, float px, float py);
+    @CriticalNative
+    private static native void nSetRotate(long nObject, float degrees);
+    @CriticalNative
+    private static native void nSetSinCos(long nObject, float sinValue, float cosValue,
+            float px, float py);
+    @CriticalNative
+    private static native void nSetSinCos(long nObject, float sinValue, float cosValue);
+    @CriticalNative
+    private static native void nSetSkew(long nObject, float kx, float ky, float px, float py);
+    @CriticalNative
+    private static native void nSetSkew(long nObject, float kx, float ky);
+    @CriticalNative
+    private static native void nSetConcat(long nObject, long nA, long nB);
+    @CriticalNative
+    private static native void nPreTranslate(long nObject, float dx, float dy);
+    @CriticalNative
+    private static native void nPreScale(long nObject, float sx, float sy, float px, float py);
+    @CriticalNative
+    private static native void nPreScale(long nObject, float sx, float sy);
+    @CriticalNative
+    private static native void nPreRotate(long nObject, float degrees, float px, float py);
+    @CriticalNative
+    private static native void nPreRotate(long nObject, float degrees);
+    @CriticalNative
+    private static native void nPreSkew(long nObject, float kx, float ky, float px, float py);
+    @CriticalNative
+    private static native void nPreSkew(long nObject, float kx, float ky);
+    @CriticalNative
+    private static native void nPreConcat(long nObject, long nOther_matrix);
+    @CriticalNative
+    private static native void nPostTranslate(long nObject, float dx, float dy);
+    @CriticalNative
+    private static native void nPostScale(long nObject, float sx, float sy, float px, float py);
+    @CriticalNative
+    private static native void nPostScale(long nObject, float sx, float sy);
+    @CriticalNative
+    private static native void nPostRotate(long nObject, float degrees, float px, float py);
+    @CriticalNative
+    private static native void nPostRotate(long nObject, float degrees);
+    @CriticalNative
+    private static native void nPostSkew(long nObject, float kx, float ky, float px, float py);
+    @CriticalNative
+    private static native void nPostSkew(long nObject, float kx, float ky);
+    @CriticalNative
+    private static native void nPostConcat(long nObject, long nOther_matrix);
+    @CriticalNative
+    private static native boolean nInvert(long nObject, long nInverse);
+    @CriticalNative
+    private static native float nMapRadius(long nObject, float radius);
+    @CriticalNative
+    private static native boolean nEquals(long nA, long nB);
 }
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 4abe50f..98d45dc 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -27,6 +27,9 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import dalvik.annotation.optimization.CriticalNative;
+import dalvik.annotation.optimization.FastNative;
+
 import java.util.HashMap;
 import java.util.Locale;
 
@@ -604,8 +607,6 @@
         return nGetFlags(mNativePaint);
     }
 
-    private native int nGetFlags(long paintPtr);
-
     /**
      * Set the paint's flags. Use the Flag enum to specific flag values.
      *
@@ -615,8 +616,6 @@
         nSetFlags(mNativePaint, flags);
     }
 
-    private native void nSetFlags(long paintPtr, int flags);
-
     /**
      * Return the paint's hinting mode.  Returns either
      * {@link #HINTING_OFF} or {@link #HINTING_ON}.
@@ -625,8 +624,6 @@
         return nGetHinting(mNativePaint);
     }
 
-    private native int nGetHinting(long paintPtr);
-
     /**
      * Set the paint's hinting mode.  May be either
      * {@link #HINTING_OFF} or {@link #HINTING_ON}.
@@ -635,8 +632,6 @@
         nSetHinting(mNativePaint, mode);
     }
 
-    private native void nSetHinting(long paintPtr, int mode);
-
     /**
      * Helper for getFlags(), returning true if ANTI_ALIAS_FLAG bit is set
      * AntiAliasing smooths out the edges of what is being drawn, but is has
@@ -661,8 +656,6 @@
         nSetAntiAlias(mNativePaint, aa);
     }
 
-    private native void nSetAntiAlias(long paintPtr, boolean aa);
-
     /**
      * Helper for getFlags(), returning true if DITHER_FLAG bit is set
      * Dithering affects how colors that are higher precision than the device
@@ -691,8 +684,6 @@
         nSetDither(mNativePaint, dither);
     }
 
-    private native void nSetDither(long paintPtr, boolean dither);
-
     /**
      * Helper for getFlags(), returning true if LINEAR_TEXT_FLAG bit is set
      *
@@ -712,8 +703,6 @@
         nSetLinearText(mNativePaint, linearText);
     }
 
-    private native void nSetLinearText(long paintPtr, boolean linearText);
-
     /**
      * Helper for getFlags(), returning true if SUBPIXEL_TEXT_FLAG bit is set
      *
@@ -733,8 +722,6 @@
         nSetSubpixelText(mNativePaint, subpixelText);
     }
 
-    private native void nSetSubpixelText(long paintPtr, boolean subpixelText);
-
     /**
      * Helper for getFlags(), returning true if UNDERLINE_TEXT_FLAG bit is set
      *
@@ -754,8 +741,6 @@
         nSetUnderlineText(mNativePaint, underlineText);
     }
 
-    private native void nSetUnderlineText(long paintPtr, boolean underlineText);
-
     /**
      * Helper for getFlags(), returning true if STRIKE_THRU_TEXT_FLAG bit is set
      *
@@ -775,8 +760,6 @@
         nSetStrikeThruText(mNativePaint, strikeThruText);
     }
 
-    private native void nSetStrikeThruText(long paintPtr, boolean strikeThruText);
-
     /**
      * Helper for getFlags(), returning true if FAKE_BOLD_TEXT_FLAG bit is set
      *
@@ -796,8 +779,6 @@
         nSetFakeBoldText(mNativePaint, fakeBoldText);
     }
 
-    private native void nSetFakeBoldText(long paintPtr, boolean fakeBoldText);
-
     /**
      * Whether or not the bitmap filter is activated.
      * Filtering affects the sampling of bitmaps when they are transformed.
@@ -823,8 +804,6 @@
         nSetFilterBitmap(mNativePaint, filter);
     }
 
-    private native void nSetFilterBitmap(long paintPtr, boolean filter);
-
     /**
      * Return the paint's style, used for controlling how primitives'
      * geometries are interpreted (except for drawBitmap, which always assumes
@@ -860,8 +839,6 @@
         return nGetColor(mNativePaint);
     }
 
-    private native int nGetColor(long paintPtr);
-
     /**
      * Set the paint's color. Note that the color is an int containing alpha
      * as well as r,g,b. This 32bit value is not premultiplied, meaning that
@@ -874,8 +851,6 @@
         nSetColor(mNativePaint, color);
     }
 
-    private native void nSetColor(long paintPtr, @ColorInt int color);
-
     /**
      * Helper to getColor() that just returns the color's alpha value. This is
      * the same as calling getColor() >>> 24. It always returns a value between
@@ -887,8 +862,6 @@
         return nGetAlpha(mNativePaint);
     }
 
-    private native int nGetAlpha(long paintPtr);
-
     /**
      * Helper to setColor(), that only assigns the color's alpha value,
      * leaving its r,g,b values unchanged. Results are undefined if the alpha
@@ -900,8 +873,6 @@
         nSetAlpha(mNativePaint, a);
     }
 
-    private native void nSetAlpha(long paintPtr, int a);
-
     /**
      * Helper to setColor(), that takes a,r,g,b and constructs the color int
      *
@@ -927,8 +898,6 @@
         return nGetStrokeWidth(mNativePaint);
     }
 
-    private native float nGetStrokeWidth(long paintPtr);
-
     /**
      * Set the width for stroking.
      * Pass 0 to stroke in hairline mode.
@@ -941,8 +910,6 @@
         nSetStrokeWidth(mNativePaint, width);
     }
 
-    private native void nSetStrokeWidth(long paintPtr, float width);
-
     /**
      * Return the paint's stroke miter value. Used to control the behavior
      * of miter joins when the joins angle is sharp.
@@ -954,8 +921,6 @@
         return nGetStrokeMiter(mNativePaint);
     }
 
-    private native float nGetStrokeMiter(long paintPtr);
-
     /**
      * Set the paint's stroke miter value. This is used to control the behavior
      * of miter joins when the joins angle is sharp. This value must be >= 0.
@@ -967,8 +932,6 @@
         nSetStrokeMiter(mNativePaint, miter);
     }
 
-    private native void nSetStrokeMiter(long paintPtr, float miter);
-
     /**
      * Return the paint's Cap, controlling how the start and end of stroked
      * lines and paths are treated.
@@ -1387,8 +1350,6 @@
         return nIsElegantTextHeight(mNativePaint);
     }
 
-    private native boolean nIsElegantTextHeight(long paintPtr);
-
     /**
      * Set the paint's elegant height metrics flag. This setting selects font
      * variants that have not been compacted to fit Latin-based vertical
@@ -1400,8 +1361,6 @@
         nSetElegantTextHeight(mNativePaint, elegant);
     }
 
-    private native void nSetElegantTextHeight(long paintPtr, boolean elegant);
-
     /**
      * Return the paint's text size.
      *
@@ -1411,8 +1370,6 @@
         return nGetTextSize(mNativePaint);
     }
 
-    private native float nGetTextSize(long paintPtr);
-
     /**
      * Set the paint's text size. This value must be > 0
      *
@@ -1422,8 +1379,6 @@
         nSetTextSize(mNativePaint, textSize);
     }
 
-    private native void nSetTextSize(long paintPtr, float textSize);
-
     /**
      * Return the paint's horizontal scale factor for text. The default value
      * is 1.0.
@@ -1434,8 +1389,6 @@
         return nGetTextScaleX(mNativePaint);
     }
 
-    private native float nGetTextScaleX(long paintPtr);
-
     /**
      * Set the paint's horizontal scale factor for text. The default value
      * is 1.0. Values > 1.0 will stretch the text wider. Values < 1.0 will
@@ -1447,8 +1400,6 @@
         nSetTextScaleX(mNativePaint, scaleX);
     }
 
-    private native void nSetTextScaleX(long paintPtr, float scaleX);
-
     /**
      * Return the paint's horizontal skew factor for text. The default value
      * is 0.
@@ -1459,8 +1410,6 @@
         return nGetTextSkewX(mNativePaint);
     }
 
-    private native float nGetTextSkewX(long paintPtr);
-
     /**
      * Set the paint's horizontal skew factor for text. The default value
      * is 0. For approximating oblique text, use values around -0.25.
@@ -1471,8 +1420,6 @@
         nSetTextSkewX(mNativePaint, skewX);
     }
 
-    private native void nSetTextSkewX(long paintPtr, float skewX);
-
     /**
      * Return the paint's letter-spacing for text. The default value
      * is 0.
@@ -1565,8 +1512,6 @@
         return nAscent(mNativePaint, mNativeTypeface);
     }
 
-    private native float nAscent(long paintPtr, long typefacePtr);
-
     /**
      * Return the distance below (positive) the baseline (descent) based on the
      * current typeface and text size.
@@ -1578,8 +1523,6 @@
         return nDescent(mNativePaint, mNativeTypeface);
     }
 
-    private native float nDescent(long paintPtr, long typefacePtr);
-
     /**
      * Class that describes the various metrics for a font at a given text size.
      * Remember, Y values increase going down, so those values will be positive,
@@ -1624,9 +1567,6 @@
         return nGetFontMetrics(mNativePaint, mNativeTypeface, metrics);
     }
 
-    private native float nGetFontMetrics(long paintPtr,
-            long typefacePtr, FontMetrics metrics);
-
     /**
      * Allocates a new FontMetrics object, and then calls getFontMetrics(fm)
      * with it, returning the object.
@@ -1686,9 +1626,6 @@
         return nGetFontMetricsInt(mNativePaint, mNativeTypeface, fmi);
     }
 
-    private native int nGetFontMetricsInt(long paintPtr,
-            long typefacePtr, FontMetricsInt fmi);
-
     public FontMetricsInt getFontMetricsInt() {
         FontMetricsInt fm = new FontMetricsInt();
         getFontMetricsInt(fm);
@@ -1860,10 +1797,6 @@
         return res;
     }
 
-    private static native int nBreakText(long nObject, long nTypeface,
-                                               char[] text, int index, int count,
-                                               float maxWidth, int bidiFlags, float[] measuredWidth);
-
     /**
      * Measure the text, stopping early if the measured width exceeds maxWidth.
      * Return the number of chars that were measured, and if measuredWidth is
@@ -1952,10 +1885,6 @@
         return res;
     }
 
-    private static native int nBreakText(long nObject, long nTypeface,
-                                        String text, boolean measureForwards,
-                                        float maxWidth, int bidiFlags, float[] measuredWidth);
-
     /**
      * Return the advance widths for the characters in the string.
      *
@@ -2679,73 +2608,34 @@
         return result;
     }
 
+    // regular JNI
+    private static native long nGetNativeFinalizer();
     private static native long nInit();
     private static native long nInitWithPaint(long paint);
-    private static native void nReset(long paintPtr);
-    private static native void nSet(long paintPtrDest, long paintPtrSrc);
-    private static native int nGetStyle(long paintPtr);
-    private static native void nSetStyle(long paintPtr, int style);
-    private static native int nGetStrokeCap(long paintPtr);
-    private static native void nSetStrokeCap(long paintPtr, int cap);
-    private static native int nGetStrokeJoin(long paintPtr);
-    private static native void nSetStrokeJoin(long paintPtr,
-                                                    int join);
-    private static native boolean nGetFillPath(long paintPtr,
-                                                     long src, long dst);
-    private static native long nSetShader(long paintPtr, long shader);
-    private static native long nSetColorFilter(long paintPtr,
-                                                    long filter);
-    private static native void nSetXfermode(long paintPtr, int xfermode);
-    private static native long nSetPathEffect(long paintPtr,
-                                                    long effect);
-    private static native long nSetMaskFilter(long paintPtr,
-                                                    long maskfilter);
-    private static native long nSetTypeface(long paintPtr,
-                                                  long typeface);
-    private static native long nSetRasterizer(long paintPtr,
-                                                   long rasterizer);
-
-    private static native int nGetTextAlign(long paintPtr);
-    private static native void nSetTextAlign(long paintPtr,
-                                                   int align);
-
-    private static native int nSetTextLocales(long paintPtr, String locales);
-    private static native void nSetTextLocalesByMinikinLangListId(long paintPtr,
-            int mMinikinLangListId);
-
+    private static native int nBreakText(long nObject, long nTypeface,
+            char[] text, int index, int count,
+            float maxWidth, int bidiFlags, float[] measuredWidth);
+    private static native int nBreakText(long nObject, long nTypeface,
+            String text, boolean measureForwards,
+            float maxWidth, int bidiFlags, float[] measuredWidth);
     private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
             char[] text, int index, int count, int contextIndex, int contextCount,
             int bidiFlags, float[] advances, int advancesIndex);
     private static native float nGetTextAdvances(long paintPtr, long typefacePtr,
             String text, int start, int end, int contextStart, int contextEnd,
             int bidiFlags, float[] advances, int advancesIndex);
-
     private native int nGetTextRunCursor(long paintPtr, char[] text,
             int contextStart, int contextLength, int dir, int offset, int cursorOpt);
     private native int nGetTextRunCursor(long paintPtr, String text,
             int contextStart, int contextEnd, int dir, int offset, int cursorOpt);
-
     private static native void nGetTextPath(long paintPtr, long typefacePtr,
             int bidiFlags, char[] text, int index, int count, float x, float y, long path);
     private static native void nGetTextPath(long paintPtr, long typefacePtr,
             int bidiFlags, String text, int start, int end, float x, float y, long path);
     private static native void nGetStringBounds(long nativePaint, long typefacePtr,
-                                String text, int start, int end, int bidiFlags, Rect bounds);
+            String text, int start, int end, int bidiFlags, Rect bounds);
     private static native void nGetCharArrayBounds(long nativePaint, long typefacePtr,
-                                char[] text, int index, int count, int bidiFlags, Rect bounds);
-    private static native long nGetNativeFinalizer();
-
-    private static native void nSetShadowLayer(long paintPtr,
-            float radius, float dx, float dy, int color);
-    private static native boolean nHasShadowLayer(long paintPtr);
-
-    private static native float nGetLetterSpacing(long paintPtr);
-    private static native void nSetLetterSpacing(long paintPtr,
-                                                       float letterSpacing);
-    private static native void nSetFontFeatureSettings(long paintPtr,
-                                                             String settings);
-    private static native int nGetHyphenEdit(long paintPtr);
-    private static native void nSetHyphenEdit(long paintPtr, int hyphen);
+            char[] text, int index, int count, int bidiFlags, Rect bounds);
     private static native boolean nHasGlyph(long paintPtr, long typefacePtr,
             int bidiFlags, String string);
     private static native float nGetRunAdvance(long paintPtr, long typefacePtr,
@@ -2754,4 +2644,134 @@
     private static native int nGetOffsetForAdvance(long paintPtr,
             long typefacePtr, char[] text, int start, int end, int contextStart, int contextEnd,
             boolean isRtl, float advance);
+
+
+    // ---------------- @FastNative ------------------------
+
+    @FastNative
+    private static native int nSetTextLocales(long paintPtr, String locales);
+    @FastNative
+    private static native void nSetFontFeatureSettings(long paintPtr, String settings);
+    @FastNative
+    private static native float nGetFontMetrics(long paintPtr,
+            long typefacePtr, FontMetrics metrics);
+    @FastNative
+    private static native int nGetFontMetricsInt(long paintPtr,
+            long typefacePtr, FontMetricsInt fmi);
+
+    
+    // ---------------- @CriticalNative ------------------------
+
+    @CriticalNative
+    private static native void nReset(long paintPtr);
+    @CriticalNative
+    private static native void nSet(long paintPtrDest, long paintPtrSrc);
+    @CriticalNative
+    private static native int nGetStyle(long paintPtr);
+    @CriticalNative
+    private static native void nSetStyle(long paintPtr, int style);
+    @CriticalNative
+    private static native int nGetStrokeCap(long paintPtr);
+    @CriticalNative
+    private static native void nSetStrokeCap(long paintPtr, int cap);
+    @CriticalNative
+    private static native int nGetStrokeJoin(long paintPtr);
+    @CriticalNative
+    private static native void nSetStrokeJoin(long paintPtr, int join);
+    @CriticalNative
+    private static native boolean nGetFillPath(long paintPtr, long src, long dst);
+    @CriticalNative
+    private static native long nSetShader(long paintPtr, long shader);
+    @CriticalNative
+    private static native long nSetColorFilter(long paintPtr, long filter);
+    @CriticalNative
+    private static native void nSetXfermode(long paintPtr, int xfermode);
+    @CriticalNative
+    private static native long nSetPathEffect(long paintPtr, long effect);
+    @CriticalNative
+    private static native long nSetMaskFilter(long paintPtr, long maskfilter);
+    @CriticalNative
+    private static native long nSetTypeface(long paintPtr, long typeface);
+    @CriticalNative
+    private static native long nSetRasterizer(long paintPtr, long rasterizer);
+    @CriticalNative
+    private static native int nGetTextAlign(long paintPtr);
+    @CriticalNative
+    private static native void nSetTextAlign(long paintPtr, int align);
+    @CriticalNative
+    private static native void nSetTextLocalesByMinikinLangListId(long paintPtr,
+            int mMinikinLangListId);
+    @CriticalNative
+    private static native void nSetShadowLayer(long paintPtr,
+            float radius, float dx, float dy, int color);
+    @CriticalNative
+    private static native boolean nHasShadowLayer(long paintPtr);
+    @CriticalNative
+    private static native float nGetLetterSpacing(long paintPtr);
+    @CriticalNative
+    private static native void nSetLetterSpacing(long paintPtr, float letterSpacing);
+    @CriticalNative
+    private static native int nGetHyphenEdit(long paintPtr);
+    @CriticalNative
+    private static native void nSetHyphenEdit(long paintPtr, int hyphen);
+    @CriticalNative
+    private static native void nSetStrokeMiter(long paintPtr, float miter);
+    @CriticalNative
+    private static native float nGetStrokeMiter(long paintPtr);
+    @CriticalNative
+    private static native void nSetStrokeWidth(long paintPtr, float width);
+    @CriticalNative
+    private static native float nGetStrokeWidth(long paintPtr);
+    @CriticalNative
+    private static native void nSetAlpha(long paintPtr, int a);
+    @CriticalNative
+    private static native void nSetDither(long paintPtr, boolean dither);
+    @CriticalNative
+    private static native int nGetFlags(long paintPtr);
+    @CriticalNative
+    private static native void nSetFlags(long paintPtr, int flags);
+    @CriticalNative
+    private static native int nGetHinting(long paintPtr);
+    @CriticalNative
+    private static native void nSetHinting(long paintPtr, int mode);
+    @CriticalNative
+    private static native void nSetAntiAlias(long paintPtr, boolean aa);
+    @CriticalNative
+    private static native void nSetLinearText(long paintPtr, boolean linearText);
+    @CriticalNative
+    private static native void nSetSubpixelText(long paintPtr, boolean subpixelText);
+    @CriticalNative
+    private static native void nSetUnderlineText(long paintPtr, boolean underlineText);
+    @CriticalNative
+    private static native void nSetFakeBoldText(long paintPtr, boolean fakeBoldText);
+    @CriticalNative
+    private static native void nSetFilterBitmap(long paintPtr, boolean filter);
+    @CriticalNative
+    private static native int nGetColor(long paintPtr);
+    @CriticalNative
+    private static native void nSetColor(long paintPtr, @ColorInt int color);
+    @CriticalNative
+    private static native int nGetAlpha(long paintPtr);
+    @CriticalNative
+    private static native void nSetStrikeThruText(long paintPtr, boolean strikeThruText);
+    @CriticalNative
+    private static native boolean nIsElegantTextHeight(long paintPtr);
+    @CriticalNative
+    private static native void nSetElegantTextHeight(long paintPtr, boolean elegant);
+    @CriticalNative
+    private static native float nGetTextSize(long paintPtr);
+    @CriticalNative
+    private static native float nGetTextScaleX(long paintPtr);
+    @CriticalNative
+    private static native void nSetTextScaleX(long paintPtr, float scaleX);
+    @CriticalNative
+    private static native float nGetTextSkewX(long paintPtr);
+    @CriticalNative
+    private static native void nSetTextSkewX(long paintPtr, float skewX);
+    @CriticalNative
+    private static native float nAscent(long paintPtr, long typefacePtr);
+    @CriticalNative
+    private static native float nDescent(long paintPtr, long typefacePtr);
+    @CriticalNative
+    private static native void nSetTextSize(long paintPtr, float textSize);
 }
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index af20f8a..10f0dda 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -109,6 +109,9 @@
  *     <li> <b>Nine Patch</b>: an extension to the PNG format allows it to
  *     specify information about how to stretch it and place things inside of
  *     it.
+ *     <li><b>Vector</b>: a drawable defined in an XML file as a set of points,
+ *     lines, and curves along with its associated color information. This type
+ *     of drawable can be scaled without loss of display quality.
  *     <li> <b>Shape</b>: contains simple drawing commands instead of a raw
  *     bitmap, allowing it to resize better in some cases.
  *     <li> <b>Layers</b>: a compound drawable, which draws multiple underlying
diff --git a/include/androidfw/AssetManager.h b/include/androidfw/AssetManager.h
index 0b22802..3b5cdce 100644
--- a/include/androidfw/AssetManager.h
+++ b/include/androidfw/AssetManager.h
@@ -59,10 +59,7 @@
  * single instance may be shared across multiple threads, and a single
  * thread may have more than one instance (the latter is discouraged).
  *
- * The purpose of the AssetManager is to create Asset objects.  To do
- * this efficiently it may cache information about the locations of
- * files it has seen.  This can be controlled with the "cacheMode"
- * argument.
+ * The purpose of the AssetManager is to create Asset objects.
  *
  * The asset hierarchy may be examined like a filesystem, using
  * AssetDir objects to peruse a single directory.
@@ -73,24 +70,16 @@
     static const char* IDMAP_BIN;
     static const char* OVERLAY_DIR;
     /*
-     * If OVERLAY_SUBDIR_PROPERTY is set, search for runtime resource overlay
-     * APKs in OVERLAY_SUBDIR/<value of OVERLAY_SUBDIR_PROPERTY>/ rather than in
+     * If OVERLAY_SKU_DIR_PROPERTY is set, search for runtime resource overlay
+     * APKs in OVERLAY_DIR/<value of OVERLAY_SKU_DIR_PROPERTY> rather than in
      * OVERLAY_DIR.
      */
-    static const char* OVERLAY_SUBDIR;
-    static const char* OVERLAY_SUBDIR_PROPERTY;
+    static const char* OVERLAY_SKU_DIR_PROPERTY;
     static const char* TARGET_PACKAGE_NAME;
     static const char* TARGET_APK_PATH;
     static const char* IDMAP_DIR;
 
-    typedef enum CacheMode {
-        CACHE_UNKNOWN = 0,
-        CACHE_OFF,          // don't try to cache file locations
-        CACHE_DEFER,        // construct cache as pieces are needed
-        //CACHE_SCAN,         // scan full(!) asset hierarchy at init() time
-    } CacheMode;
-
-    AssetManager(CacheMode cacheMode = CACHE_OFF);
+    AssetManager();
     virtual ~AssetManager(void);
 
     static int32_t getGlobalCount();
@@ -127,23 +116,16 @@
     int32_t nextAssetPath(const int32_t cookie) const;
 
     /*
-     * Return an asset path in the manager.  'which' must be between 0 and
-     * countAssetPaths().
+     * Return an asset path in the manager.  'cookie' must be a non-negative value
+     * previously returned from addAssetPath() or nextAssetPath().
      */
     String8 getAssetPath(const int32_t cookie) const;
 
     /*
-     * Set the current locale and vendor.  The locale can change during
-     * the lifetime of an AssetManager if the user updates the device's
-     * language setting.  The vendor is less likely to change.
-     *
-     * Pass in NULL to indicate no preference.
-     */
-    void setLocale(const char* locale);
-    void setVendor(const char* vendor);
-
-    /*
-     * Choose screen orientation for resources values returned.
+     * Sets various device configuration parameters, like screen orientation, layout,
+     * size, locale, etc.
+     * The optional 'locale' string takes precedence over the locale within 'config'
+     * and must be in bcp47 format.
      */
     void setConfiguration(const ResTable_config& config, const char* locale = NULL);
 
@@ -154,9 +136,6 @@
     /*
      * Open an asset.
      *
-     * This will search through locale-specific and vendor-specific
-     * directories and packages to find the file.
-     *
      * The object returned does not depend on the AssetManager.  It should
      * be freed by calling Asset::close().
      */
@@ -166,9 +145,8 @@
      * Open a non-asset file as an asset.
      *
      * This is for opening files that are included in an asset package
-     * but aren't assets.  These sit outside the usual "locale/vendor"
-     * path hierarchy, and will not be seen by "AssetDir" or included
-     * in our filename cache.
+     * but aren't assets.  These sit outside the usual "assets/"
+     * path hierarchy, and will not be seen by "AssetDir".
      */
     Asset* openNonAsset(const char* fileName, AccessMode mode, int32_t* outCookie = NULL);
 
@@ -181,11 +159,6 @@
     /*
      * Open a directory within the asset hierarchy.
      *
-     * The contents of the directory are an amalgam of vendor-specific,
-     * locale-specific, and generic assets stored loosely or in asset
-     * packages.  Depending on the cache setting and previous accesses,
-     * this call may incur significant disk overhead.
-     *
      * To open the top-level directory, pass in "".
      */
     AssetDir* openDir(const char* dirName);
@@ -193,11 +166,6 @@
     /*
      * Open a directory within a particular path of the asset manager.
      *
-     * The contents of the directory are an amalgam of vendor-specific,
-     * locale-specific, and generic assets stored loosely or in asset
-     * packages.  Depending on the cache setting and previous accesses,
-     * this call may incur significant disk overhead.
-     *
      * To open the top-level directory, pass in "".
      */
     AssetDir* openNonAssetDir(const int32_t cookie, const char* dirName);
@@ -216,13 +184,6 @@
     const ResTable& getResources(bool required = true) const;
 
     /*
-     * Discard cached filename information.  This only needs to be called
-     * if somebody has updated the set of "loose" files, and we want to
-     * discard our cached notion of what's where.
-     */
-    void purge(void) { purgeFileNameCacheLocked(); }
-
-    /*
      * Return true if the files this AssetManager references are all
      * up-to-date (have not been changed since it was created).  If false
      * is returned, you will need to create a new AssetManager to get
@@ -254,14 +215,8 @@
         bool isSystemAsset;
     };
 
-    Asset* openInPathLocked(const char* fileName, AccessMode mode,
-        const asset_path& path);
     Asset* openNonAssetInPathLocked(const char* fileName, AccessMode mode,
         const asset_path& path);
-    Asset* openInLocaleVendorLocked(const char* fileName, AccessMode mode,
-        const asset_path& path, const char* locale, const char* vendor);
-    String8 createPathNameLocked(const asset_path& path, const char* locale,
-        const char* vendor);
     String8 createPathNameLocked(const asset_path& path, const char* rootDir);
     String8 createZipSourceNameLocked(const String8& zipFileName,
         const String8& dirName, const String8& fileName);
@@ -279,15 +234,6 @@
     void mergeInfoLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
         const SortedVector<AssetDir::FileInfo>* pContents);
 
-    void loadFileNameCacheLocked(void);
-    void fncScanLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
-        const char* dirName);
-    bool fncScanAndMergeDirLocked(
-        SortedVector<AssetDir::FileInfo>* pMergedInfo,
-        const asset_path& path, const char* locale, const char* vendor,
-        const char* dirName);
-    void purgeFileNameCacheLocked(void);
-
     const ResTable* getResTable(bool required = true) const;
     void setLocaleLocked(const char* locale);
     void updateResourceParamsLocked() const;
@@ -344,8 +290,8 @@
      */
     class ZipSet {
     public:
-        ZipSet(void);
-        ~ZipSet(void);
+        ZipSet() = default;
+        ~ZipSet();
 
         /*
          * Return a ZipFileRO structure for a ZipFileRO with the specified
@@ -382,23 +328,9 @@
 
     Vector<asset_path> mAssetPaths;
     char*           mLocale;
-    char*           mVendor;
 
     mutable ResTable* mResources;
     ResTable_config* mConfig;
-
-    /*
-     * Cached data for "loose" files.  This lets us avoid poking at the
-     * filesystem when searching for loose assets.  Each entry is the
-     * "extended partial" path, e.g. "default/default/foo/bar.txt".  The
-     * full set of files is present, including ".EXCLUDE" entries.
-     *
-     * We do not cache directory names.  We don't retain the ".gz",
-     * because to our clients "foo" and "foo.gz" both look like "foo".
-     */
-    CacheMode       mCacheMode;         // is the cache enabled?
-    bool            mCacheValid;        // clear when locale or vendor changes
-    SortedVector<AssetDir::FileInfo> mCache;
 };
 
 }; // namespace android
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index fe78cef..796cb36 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -56,12 +56,6 @@
 
 static const bool kIsDebug = false;
 
-/*
- * Names for default app, locale, and vendor.  We might want to change
- * these to be an actual locale, e.g. always use en-US as the default.
- */
-static const char* kDefaultLocale = "default";
-static const char* kDefaultVendor = "default";
 static const char* kAssetsRoot = "assets";
 static const char* kAppZipName = NULL; //"classes.jar";
 static const char* kSystemAssets = "framework/framework-res.apk";
@@ -76,75 +70,70 @@
 const char* AssetManager::RESOURCES_FILENAME = "resources.arsc";
 const char* AssetManager::IDMAP_BIN = "/system/bin/idmap";
 const char* AssetManager::OVERLAY_DIR = "/vendor/overlay";
+const char* AssetManager::OVERLAY_SKU_DIR_PROPERTY = "ro.boot.vendor.overlay.sku";
 const char* AssetManager::TARGET_PACKAGE_NAME = "android";
 const char* AssetManager::TARGET_APK_PATH = "/system/framework/framework-res.apk";
-const char* AssetManager::OVERLAY_SUBDIR = "/system/vendor/overlay-subdir";
-const char* AssetManager::OVERLAY_SUBDIR_PROPERTY = "ro.boot.vendor.overlay.subdir";
 const char* AssetManager::IDMAP_DIR = "/data/resource-cache";
 
 namespace {
-    String8 idmapPathForPackagePath(const String8& pkgPath)
-    {
-        const char* root = getenv("ANDROID_DATA");
-        LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_DATA not set");
-        String8 path(root);
-        path.appendPath(kResourceCache);
 
-        char buf[256]; // 256 chars should be enough for anyone...
-        strncpy(buf, pkgPath.string(), 255);
-        buf[255] = '\0';
-        char* filename = buf;
-        while (*filename && *filename == '/') {
-            ++filename;
-        }
-        char* p = filename;
-        while (*p) {
-            if (*p == '/') {
-                *p = '@';
-            }
-            ++p;
-        }
-        path.appendPath(filename);
-        path.append("@idmap");
+String8 idmapPathForPackagePath(const String8& pkgPath) {
+    const char* root = getenv("ANDROID_DATA");
+    LOG_ALWAYS_FATAL_IF(root == NULL, "ANDROID_DATA not set");
+    String8 path(root);
+    path.appendPath(kResourceCache);
 
-        return path;
+    char buf[256]; // 256 chars should be enough for anyone...
+    strncpy(buf, pkgPath.string(), 255);
+    buf[255] = '\0';
+    char* filename = buf;
+    while (*filename && *filename == '/') {
+        ++filename;
     }
-
-    /*
-     * Like strdup(), but uses C++ "new" operator instead of malloc.
-     */
-    static char* strdupNew(const char* str)
-    {
-        char* newStr;
-        int len;
-
-        if (str == NULL)
-            return NULL;
-
-        len = strlen(str);
-        newStr = new char[len+1];
-        memcpy(newStr, str, len+1);
-
-        return newStr;
+    char* p = filename;
+    while (*p) {
+        if (*p == '/') {
+            *p = '@';
+        }
+        ++p;
     }
+    path.appendPath(filename);
+    path.append("@idmap");
+
+    return path;
 }
 
 /*
+ * Like strdup(), but uses C++ "new" operator instead of malloc.
+ */
+static char* strdupNew(const char* str) {
+    char* newStr;
+    int len;
+
+    if (str == NULL)
+        return NULL;
+
+    len = strlen(str);
+    newStr = new char[len+1];
+    memcpy(newStr, str, len+1);
+
+    return newStr;
+}
+
+} // namespace
+
+/*
  * ===========================================================================
  *      AssetManager
  * ===========================================================================
  */
 
-int32_t AssetManager::getGlobalCount()
-{
+int32_t AssetManager::getGlobalCount() {
     return gCount;
 }
 
-AssetManager::AssetManager(CacheMode cacheMode)
-    : mLocale(NULL), mVendor(NULL),
-      mResources(NULL), mConfig(new ResTable_config),
-      mCacheMode(cacheMode), mCacheValid(false)
-{
+AssetManager::AssetManager() :
+        mLocale(NULL), mResources(NULL), mConfig(new ResTable_config) {
     int count = android_atomic_inc(&gCount) + 1;
     if (kIsDebug) {
         ALOGI("Creating AssetManager %p #%d\n", this, count);
@@ -152,8 +141,7 @@
     memset(mConfig, 0, sizeof(ResTable_config));
 }
 
-AssetManager::~AssetManager(void)
-{
+AssetManager::~AssetManager() {
     int count = android_atomic_dec(&gCount);
     if (kIsDebug) {
         ALOGI("Destroying AssetManager in %p #%d\n", this, count);
@@ -164,12 +152,10 @@
 
     // don't have a String class yet, so make sure we clean up
     delete[] mLocale;
-    delete[] mVendor;
 }
 
 bool AssetManager::addAssetPath(
-        const String8& path, int32_t* cookie, bool appAsLib, bool isSystemAsset)
-{
+        const String8& path, int32_t* cookie, bool appAsLib, bool isSystemAsset) {
     AutoMutex _l(mLock);
 
     asset_path ap;
@@ -344,25 +330,9 @@
     return String8();
 }
 
-/*
- * Set the current locale.  Use NULL to indicate no locale.
- *
- * Close and reopen Zip archives as appropriate, and reset cached
- * information in the locale-specific sections of the tree.
- */
-void AssetManager::setLocale(const char* locale)
-{
-    AutoMutex _l(mLock);
-    setLocaleLocked(locale);
-}
-
-
 void AssetManager::setLocaleLocked(const char* locale)
 {
     if (mLocale != NULL) {
-        /* previously set, purge cached data */
-        purgeFileNameCacheLocked();
-        //mZipSet.purgeLocale();
         delete[] mLocale;
     }
 
@@ -370,25 +340,6 @@
     updateResourceParamsLocked();
 }
 
-/*
- * Set the current vendor.  Use NULL to indicate no vendor.
- *
- * Close and reopen Zip archives as appropriate, and reset cached
- * information in the vendor-specific sections of the tree.
- */
-void AssetManager::setVendor(const char* vendor)
-{
-    AutoMutex _l(mLock);
-
-    if (mVendor != NULL) {
-        /* previously set, purge cached data */
-        purgeFileNameCacheLocked();
-        //mZipSet.purgeVendor();
-        delete[] mVendor;
-    }
-    mVendor = strdupNew(vendor);
-}
-
 void AssetManager::setConfiguration(const ResTable_config& config, const char* locale)
 {
     AutoMutex _l(mLock);
@@ -413,23 +364,11 @@
 /*
  * Open an asset.
  *
- * The data could be;
- *  - In a file on disk (assetBase + fileName).
- *  - In a compressed file on disk (assetBase + fileName.gz).
- *  - In a Zip archive, uncompressed or compressed.
+ * The data could be in any asset path. Each asset path could be:
+ *  - A directory on disk.
+ *  - A Zip archive, uncompressed or compressed.
  *
- * It can be in a number of different directories and Zip archives.
- * The search order is:
- *  - [appname]
- *    - locale + vendor
- *    - "default" + vendor
- *    - locale + "default"
- *    - "default + "default"
- *  - "common"
- *    - (same as above)
- *
- * To find a particular file, we have to try up to eight paths with
- * all three forms of data.
+ * If the file is in a directory, it could have a .gz suffix, meaning it is compressed.
  *
  * We should probably reject requests for "illegal" filenames, e.g. those
  * with illegal characters or "../" backward relative paths.
@@ -440,10 +379,6 @@
 
     LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
 
-
-    if (mCacheMode != CACHE_OFF && !mCacheValid)
-        loadFileNameCacheLocked();
-
     String8 assetName(kAssetsRoot);
     assetName.appendPath(fileName);
 
@@ -468,8 +403,7 @@
 /*
  * Open a non-asset file as if it were an asset.
  *
- * The "fileName" is the partial path starting from the application
- * name.
+ * The "fileName" is the partial path starting from the application name.
  */
 Asset* AssetManager::openNonAsset(const char* fileName, AccessMode mode, int32_t* outCookie)
 {
@@ -477,10 +411,6 @@
 
     LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
 
-
-    if (mCacheMode != CACHE_OFF && !mCacheValid)
-        loadFileNameCacheLocked();
-
     /*
      * For each top-level asset path, search for the asset.
      */
@@ -508,9 +438,6 @@
 
     LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
 
-    if (mCacheMode != CACHE_OFF && !mCacheValid)
-        loadFileNameCacheLocked();
-
     if (which < mAssetPaths.size()) {
         ALOGV("Looking for non-asset '%s' in '%s'\n", fileName,
                 mAssetPaths.itemAt(which).path.string());
@@ -542,10 +469,11 @@
     pAsset = open(fileName, Asset::ACCESS_STREAMING);
     delete pAsset;
 
-    if (pAsset == NULL)
+    if (pAsset == NULL) {
         return kFileTypeNonexistent;
-    else
+    } else {
         return kFileTypeRegular;
+    }
 }
 
 bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) const {
@@ -660,10 +588,6 @@
         LOG_FATAL_IF(mAssetPaths.size() == 0, "No assets added to AssetManager");
     }
 
-    if (mCacheMode != CACHE_OFF && !mCacheValid) {
-        const_cast<AssetManager*>(this)->loadFileNameCacheLocked();
-    }
-
     mResources = new ResTable();
     updateResourceParamsLocked();
 
@@ -834,158 +758,6 @@
 }
 
 /*
- * Open an asset, searching for it in the directory hierarchy for the
- * specified app.
- *
- * Pass in a NULL values for "appName" if the common app directory should
- * be used.
- */
-Asset* AssetManager::openInPathLocked(const char* fileName, AccessMode mode,
-    const asset_path& ap)
-{
-    Asset* pAsset = NULL;
-
-    /*
-     * Try various combinations of locale and vendor.
-     */
-    if (mLocale != NULL && mVendor != NULL)
-        pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, mVendor);
-    if (pAsset == NULL && mVendor != NULL)
-        pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, mVendor);
-    if (pAsset == NULL && mLocale != NULL)
-        pAsset = openInLocaleVendorLocked(fileName, mode, ap, mLocale, NULL);
-    if (pAsset == NULL)
-        pAsset = openInLocaleVendorLocked(fileName, mode, ap, NULL, NULL);
-
-    return pAsset;
-}
-
-/*
- * Open an asset, searching for it in the directory hierarchy for the
- * specified locale and vendor.
- *
- * We also search in "app.jar".
- *
- * Pass in NULL values for "appName", "locale", and "vendor" if the
- * defaults should be used.
- */
-Asset* AssetManager::openInLocaleVendorLocked(const char* fileName, AccessMode mode,
-    const asset_path& ap, const char* locale, const char* vendor)
-{
-    Asset* pAsset = NULL;
-
-    if (ap.type == kFileTypeDirectory) {
-        if (mCacheMode == CACHE_OFF) {
-            /* look at the filesystem on disk */
-            String8 path(createPathNameLocked(ap, locale, vendor));
-            path.appendPath(fileName);
-    
-            String8 excludeName(path);
-            excludeName.append(kExcludeExtension);
-            if (::getFileType(excludeName.string()) != kFileTypeNonexistent) {
-                /* say no more */
-                //printf("+++ excluding '%s'\n", (const char*) excludeName);
-                return kExcludedAsset;
-            }
-    
-            pAsset = openAssetFromFileLocked(path, mode);
-    
-            if (pAsset == NULL) {
-                /* try again, this time with ".gz" */
-                path.append(".gz");
-                pAsset = openAssetFromFileLocked(path, mode);
-            }
-    
-            if (pAsset != NULL)
-                pAsset->setAssetSource(path);
-        } else {
-            /* find in cache */
-            String8 path(createPathNameLocked(ap, locale, vendor));
-            path.appendPath(fileName);
-    
-            AssetDir::FileInfo tmpInfo;
-            bool found = false;
-    
-            String8 excludeName(path);
-            excludeName.append(kExcludeExtension);
-    
-            if (mCache.indexOf(excludeName) != NAME_NOT_FOUND) {
-                /* go no farther */
-                //printf("+++ Excluding '%s'\n", (const char*) excludeName);
-                return kExcludedAsset;
-            }
-
-            /*
-             * File compression extensions (".gz") don't get stored in the
-             * name cache, so we have to try both here.
-             */
-            if (mCache.indexOf(path) != NAME_NOT_FOUND) {
-                found = true;
-                pAsset = openAssetFromFileLocked(path, mode);
-                if (pAsset == NULL) {
-                    /* try again, this time with ".gz" */
-                    path.append(".gz");
-                    pAsset = openAssetFromFileLocked(path, mode);
-                }
-            }
-
-            if (pAsset != NULL)
-                pAsset->setAssetSource(path);
-
-            /*
-             * Don't continue the search into the Zip files.  Our cached info
-             * said it was a file on disk; to be consistent with openDir()
-             * we want to return the loose asset.  If the cached file gets
-             * removed, we fail.
-             *
-             * The alternative is to update our cache when files get deleted,
-             * or make some sort of "best effort" promise, but for now I'm
-             * taking the hard line.
-             */
-            if (found) {
-                if (pAsset == NULL)
-                    ALOGD("Expected file not found: '%s'\n", path.string());
-                return pAsset;
-            }
-        }
-    }
-
-    /*
-     * Either it wasn't found on disk or on the cached view of the disk.
-     * Dig through the currently-opened set of Zip files.  If caching
-     * is disabled, the Zip file may get reopened.
-     */
-    if (pAsset == NULL && ap.type == kFileTypeRegular) {
-        String8 path;
-
-        path.appendPath((locale != NULL) ? locale : kDefaultLocale);
-        path.appendPath((vendor != NULL) ? vendor : kDefaultVendor);
-        path.appendPath(fileName);
-
-        /* check the appropriate Zip file */
-        ZipFileRO* pZip = getZipFileLocked(ap);
-        if (pZip != NULL) {
-            //printf("GOT zip, checking '%s'\n", (const char*) path);
-            ZipEntryRO entry = pZip->findEntryByName(path.string());
-            if (entry != NULL) {
-                //printf("FOUND in Zip file for %s/%s-%s\n",
-                //    appName, locale, vendor);
-                pAsset = openAssetFromZipLocked(pZip, entry, mode, path);
-                pZip->releaseEntry(entry);
-            }
-        }
-
-        if (pAsset != NULL) {
-            /* create a "source" name, for debug/display */
-            pAsset->setAssetSource(createZipSourceNameLocked(ZipSet::getPathName(ap.path.string()),
-                                                             String8(""), String8(fileName)));
-        }
-    }
-
-    return pAsset;
-}
-
-/*
  * Create a "source name" for a file from a Zip archive.
  */
 String8 AssetManager::createZipSourceNameLocked(const String8& zipFileName,
@@ -1002,18 +774,6 @@
 }
 
 /*
- * Create a path to a loose asset (asset-base/app/locale/vendor).
- */
-String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* locale,
-    const char* vendor)
-{
-    String8 path(ap.path);
-    path.appendPath((locale != NULL) ? locale : kDefaultLocale);
-    path.appendPath((vendor != NULL) ? vendor : kDefaultVendor);
-    return path;
-}
-
-/*
  * Create a path to a loose asset (asset-base/app/rootDir).
  */
 String8 AssetManager::createPathNameLocked(const asset_path& ap, const char* rootDir)
@@ -1026,15 +786,6 @@
 /*
  * Return a pointer to one of our open Zip archives.  Returns NULL if no
  * matching Zip file exists.
- *
- * Right now we have 2 possible Zip files (1 each in app/"common").
- *
- * If caching is set to CACHE_OFF, to get the expected behavior we
- * need to reopen the Zip file on every request.  That would be silly
- * and expensive, so instead we just check the file modification date.
- *
- * Pass in NULL values for "appName", "locale", and "vendor" if the
- * generics should be used.
  */
 ZipFileRO* AssetManager::getZipFileLocked(const asset_path& ap)
 {
@@ -1119,14 +870,10 @@
     return pAsset;
 }
 
-
-
 /*
  * Open a directory in the asset namespace.
  *
- * An "asset directory" is simply the combination of all files in all
- * locations, with ".gz" stripped for loose files.  With app, locale, and
- * vendor defined, we have 8 directories and 2 Zip archives to scan.
+ * An "asset directory" is simply the combination of all asset paths' "assets/" directories.
  *
  * Pass in "" for the root dir.
  */
@@ -1142,9 +889,6 @@
 
     //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase);
 
-    if (mCacheMode != CACHE_OFF && !mCacheValid)
-        loadFileNameCacheLocked();
-
     pDir = new AssetDir;
 
     /*
@@ -1187,9 +931,7 @@
 /*
  * Open a directory in the non-asset namespace.
  *
- * An "asset directory" is simply the combination of all files in all
- * locations, with ".gz" stripped for loose files.  With app, locale, and
- * vendor defined, we have 8 directories and 2 Zip archives to scan.
+ * An "asset directory" is simply the combination of all asset paths' "assets/" directories.
  *
  * Pass in "" for the root dir.
  */
@@ -1205,9 +947,6 @@
 
     //printf("+++ openDir(%s) in '%s'\n", dirName, (const char*) mAssetBase);
 
-    if (mCacheMode != CACHE_OFF && !mCacheValid)
-        loadFileNameCacheLocked();
-
     pDir = new AssetDir;
 
     pMergedInfo = new SortedVector<AssetDir::FileInfo>;
@@ -1248,74 +987,17 @@
 bool AssetManager::scanAndMergeDirLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
     const asset_path& ap, const char* rootDir, const char* dirName)
 {
-    SortedVector<AssetDir::FileInfo>* pContents;
-    String8 path;
-
     assert(pMergedInfo != NULL);
 
-    //printf("scanAndMergeDir: %s %s %s %s\n", appName, locale, vendor,dirName);
+    //printf("scanAndMergeDir: %s %s %s\n", ap.path.string(), rootDir, dirName);
 
-    if (mCacheValid) {
-        int i, start, count;
+    String8 path = createPathNameLocked(ap, rootDir);
+    if (dirName[0] != '\0')
+        path.appendPath(dirName);
 
-        pContents = new SortedVector<AssetDir::FileInfo>;
-
-        /*
-         * Get the basic partial path and find it in the cache.  That's
-         * the start point for the search.
-         */
-        path = createPathNameLocked(ap, rootDir);
-        if (dirName[0] != '\0')
-            path.appendPath(dirName);
-
-        start = mCache.indexOf(path);
-        if (start == NAME_NOT_FOUND) {
-            //printf("+++ not found in cache: dir '%s'\n", (const char*) path);
-            delete pContents;
-            return false;
-        }
-
-        /*
-         * The match string looks like "common/default/default/foo/bar/".
-         * The '/' on the end ensures that we don't match on the directory
-         * itself or on ".../foo/barfy/".
-         */
-        path.append("/");
-
-        count = mCache.size();
-
-        /*
-         * Pick out the stuff in the current dir by examining the pathname.
-         * It needs to match the partial pathname prefix, and not have a '/'
-         * (fssep) anywhere after the prefix.
-         */
-        for (i = start+1; i < count; i++) {
-            if (mCache[i].getFileName().length() > path.length() &&
-                strncmp(mCache[i].getFileName().string(), path.string(), path.length()) == 0)
-            {
-                const char* name = mCache[i].getFileName().string();
-                // XXX THIS IS BROKEN!  Looks like we need to store the full
-                // path prefix separately from the file path.
-                if (strchr(name + path.length(), '/') == NULL) {
-                    /* grab it, reducing path to just the filename component */
-                    AssetDir::FileInfo tmp = mCache[i];
-                    tmp.setFileName(tmp.getFileName().getPathLeaf());
-                    pContents->add(tmp);
-                }
-            } else {
-                /* no longer in the dir or its subdirs */
-                break;
-            }
-
-        }
-    } else {
-        path = createPathNameLocked(ap, rootDir);
-        if (dirName[0] != '\0')
-            path.appendPath(dirName);
-        pContents = scanDirLocked(path);
-        if (pContents == NULL)
-            return false;
-    }
+    SortedVector<AssetDir::FileInfo>* pContents = scanDirLocked(path);
+    if (pContents == NULL)
+        return false;
 
     // if we wanted to do an incremental cache fill, we would do it here
 
@@ -1642,153 +1324,6 @@
 #endif
 }
 
-
-/*
- * Load all files into the file name cache.  We want to do this across
- * all combinations of { appname, locale, vendor }, performing a recursive
- * directory traversal.
- *
- * This is not the most efficient data structure.  Also, gathering the
- * information as we needed it (file-by-file or directory-by-directory)
- * would be faster.  However, on the actual device, 99% of the files will
- * live in Zip archives, so this list will be very small.  The trouble
- * is that we have to check the "loose" files first, so it's important
- * that we don't beat the filesystem silly looking for files that aren't
- * there.
- *
- * Note on thread safety: this is the only function that causes updates
- * to mCache, and anybody who tries to use it will call here if !mCacheValid,
- * so we need to employ a mutex here.
- */
-void AssetManager::loadFileNameCacheLocked(void)
-{
-    assert(!mCacheValid);
-    assert(mCache.size() == 0);
-
-#ifdef DO_TIMINGS   // need to link against -lrt for this now
-    DurationTimer timer;
-    timer.start();
-#endif
-
-    fncScanLocked(&mCache, "");
-
-#ifdef DO_TIMINGS
-    timer.stop();
-    ALOGD("Cache scan took %.3fms\n",
-        timer.durationUsecs() / 1000.0);
-#endif
-
-#if 0
-    int i;
-    printf("CACHED FILE LIST (%d entries):\n", mCache.size());
-    for (i = 0; i < (int) mCache.size(); i++) {
-        printf(" %d: (%d) '%s'\n", i,
-            mCache.itemAt(i).getFileType(),
-            (const char*) mCache.itemAt(i).getFileName());
-    }
-#endif
-
-    mCacheValid = true;
-}
-
-/*
- * Scan up to 8 versions of the specified directory.
- */
-void AssetManager::fncScanLocked(SortedVector<AssetDir::FileInfo>* pMergedInfo,
-    const char* dirName)
-{
-    size_t i = mAssetPaths.size();
-    while (i > 0) {
-        i--;
-        const asset_path& ap = mAssetPaths.itemAt(i);
-        fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, NULL, dirName);
-        if (mLocale != NULL)
-            fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, NULL, dirName);
-        if (mVendor != NULL)
-            fncScanAndMergeDirLocked(pMergedInfo, ap, NULL, mVendor, dirName);
-        if (mLocale != NULL && mVendor != NULL)
-            fncScanAndMergeDirLocked(pMergedInfo, ap, mLocale, mVendor, dirName);
-    }
-}
-
-/*
- * Recursively scan this directory and all subdirs.
- *
- * This is similar to scanAndMergeDir, but we don't remove the .EXCLUDE
- * files, and we prepend the extended partial path to the filenames.
- */
-bool AssetManager::fncScanAndMergeDirLocked(
-    SortedVector<AssetDir::FileInfo>* pMergedInfo,
-    const asset_path& ap, const char* locale, const char* vendor,
-    const char* dirName)
-{
-    SortedVector<AssetDir::FileInfo>* pContents;
-    String8 partialPath;
-    String8 fullPath;
-
-    // XXX This is broken -- the filename cache needs to hold the base
-    // asset path separately from its filename.
-    
-    partialPath = createPathNameLocked(ap, locale, vendor);
-    if (dirName[0] != '\0') {
-        partialPath.appendPath(dirName);
-    }
-
-    fullPath = partialPath;
-    pContents = scanDirLocked(fullPath);
-    if (pContents == NULL) {
-        return false;       // directory did not exist
-    }
-
-    /*
-     * Scan all subdirectories of the current dir, merging what we find
-     * into "pMergedInfo".
-     */
-    for (int i = 0; i < (int) pContents->size(); i++) {
-        if (pContents->itemAt(i).getFileType() == kFileTypeDirectory) {
-            String8 subdir(dirName);
-            subdir.appendPath(pContents->itemAt(i).getFileName());
-
-            fncScanAndMergeDirLocked(pMergedInfo, ap, locale, vendor, subdir.string());
-        }
-    }
-
-    /*
-     * To be consistent, we want entries for the root directory.  If
-     * we're the root, add one now.
-     */
-    if (dirName[0] == '\0') {
-        AssetDir::FileInfo tmpInfo;
-
-        tmpInfo.set(String8(""), kFileTypeDirectory);
-        tmpInfo.setSourceName(createPathNameLocked(ap, locale, vendor));
-        pContents->add(tmpInfo);
-    }
-
-    /*
-     * We want to prepend the extended partial path to every entry in
-     * "pContents".  It's the same value for each entry, so this will
-     * not change the sorting order of the vector contents.
-     */
-    for (int i = 0; i < (int) pContents->size(); i++) {
-        const AssetDir::FileInfo& info = pContents->itemAt(i);
-        pContents->editItemAt(i).setFileName(partialPath.appendPathCopy(info.getFileName()));
-    }
-
-    mergeInfoLocked(pMergedInfo, pContents);
-    delete pContents;
-    return true;
-}
-
-/*
- * Trash the cache.
- */
-void AssetManager::purgeFileNameCacheLocked(void)
-{
-    mCacheValid = false;
-    mCache.clear();
-}
-
 /*
  * ===========================================================================
  *      AssetManager::SharedZip
@@ -1838,6 +1373,7 @@
 
 Asset* AssetManager::SharedZip::getResourceTableAsset()
 {
+    AutoMutex _l(gLock);
     ALOGV("Getting from SharedZip %p resource asset %p\n", this, mResourceTableAsset);
     return mResourceTableAsset;
 }
@@ -1847,10 +1383,10 @@
     {
         AutoMutex _l(gLock);
         if (mResourceTableAsset == NULL) {
-            mResourceTableAsset = asset;
             // This is not thread safe the first time it is called, so
             // do it here with the global lock held.
             asset->getBuffer(true);
+            mResourceTableAsset = asset;
             return asset;
         }
     }
@@ -1921,13 +1457,6 @@
  */
 
 /*
- * Constructor.
- */
-AssetManager::ZipSet::ZipSet(void)
-{
-}
-
-/*
  * Destructor.  Close any open archives.
  */
 AssetManager::ZipSet::~ZipSet(void)
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index dd20a76..8c36ab5 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -229,19 +229,6 @@
     }
 }
 
-static OffscreenBuffer* createLayer(RenderState& renderState, uint32_t width, uint32_t height) {
-    return renderState.layerPool().get(renderState, width, height);
-}
-
-static void destroyLayer(OffscreenBuffer* layer) {
-    RenderState& renderState = layer->renderState;
-    renderState.layerPool().putOrDelete(layer);
-}
-
-static bool layerMatchesWidthAndHeight(OffscreenBuffer* layer, int width, int height) {
-    return layer->viewportWidth == (uint32_t) width && layer->viewportHeight == (uint32_t)height;
-}
-
 void RenderNode::pushLayerUpdate(TreeInfo& info) {
     LayerType layerType = properties().effectiveLayerType();
     // If we are not a layer OR we cannot be rendered (eg, view was detached)
@@ -251,34 +238,15 @@
             || CC_UNLIKELY(properties().getWidth() == 0)
             || CC_UNLIKELY(properties().getHeight() == 0)) {
         if (CC_UNLIKELY(mLayer)) {
-            destroyLayer(mLayer);
-            mLayer = nullptr;
+            renderthread::CanvasContext::destroyLayer(this);
         }
         return;
     }
 
-    bool transformUpdateNeeded = false;
-    if (!mLayer) {
-        mLayer = createLayer(info.canvasContext.getRenderState(), getWidth(), getHeight());
+    if(info.canvasContext.createOrUpdateLayer(this, *info.damageAccumulator)) {
         damageSelf(info);
-        transformUpdateNeeded = true;
-    } else if (!layerMatchesWidthAndHeight(mLayer, getWidth(), getHeight())) {
-        // TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering)
-        // Or, ideally, maintain damage between frames on node/layer so ordering is always correct
-        RenderState& renderState = mLayer->renderState;
-        if (properties().fitsOnLayer()) {
-            mLayer = renderState.layerPool().resize(mLayer, getWidth(), getHeight());
-        } else {
-            destroyLayer(mLayer);
-            mLayer = nullptr;
-        }
-        damageSelf(info);
-        transformUpdateNeeded = true;
     }
 
-    SkRect dirty;
-    info.damageAccumulator->peekAtDirty(&dirty);
-
     if (!mLayer) {
         Caches::getInstance().dumpMemoryUsage();
         if (info.errorHandler) {
@@ -296,13 +264,8 @@
         return;
     }
 
-    if (transformUpdateNeeded && mLayer) {
-        // update the transform in window of the layer to reset its origin wrt light source position
-        Matrix4 windowTransform;
-        info.damageAccumulator->computeCurrentTransform(&windowTransform);
-        mLayer->setWindowTransform(windowTransform);
-    }
-
+    SkRect dirty;
+    info.damageAccumulator->peekAtDirty(&dirty);
     info.layerUpdateQueue->enqueueLayerWithDamage(this, dirty);
 
     // There might be prefetched layers that need to be accounted for.
@@ -451,8 +414,7 @@
 
 void RenderNode::destroyHardwareResources(TreeObserver* observer, TreeInfo* info) {
     if (mLayer) {
-        destroyLayer(mLayer);
-        mLayer = nullptr;
+        renderthread::CanvasContext::destroyLayer(this);
     }
     if (mDisplayList) {
         for (auto&& child : mDisplayList->getChildren()) {
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index a0679b1..da93c13 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -199,6 +199,7 @@
     }
     OffscreenBuffer* getLayer() const { return mLayer; }
     OffscreenBuffer** getLayerHandle() { return &mLayer; } // ugh...
+    void setLayer(OffscreenBuffer* layer) { mLayer = layer; }
 
     // Note: The position callbacks are relying on the listener using
     // the frameNumber to appropriately batch/synchronize these transactions.
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 43471e5..0f2d55b 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -85,6 +85,18 @@
     return nullptr;
 }
 
+void CanvasContext::destroyLayer(RenderNode* node) {
+    auto renderType = Properties::getRenderPipelineType();
+    switch (renderType) {
+        case RenderPipelineType::OpenGL:
+            OpenGLPipeline::destroyLayer(node);
+            break;
+        default:
+            LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t) renderType);
+            break;
+    }
+}
+
 CanvasContext::CanvasContext(RenderThread& thread, bool translucent,
         RenderNode* rootRenderNode, IContextFactory* contextFactory,
         std::unique_ptr<IRenderPipeline> renderPipeline)
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 7ebe0ae..652cddd 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -68,6 +68,23 @@
             RenderNode* rootRenderNode, IContextFactory* contextFactory);
     virtual ~CanvasContext();
 
+    /**
+     * Update or create a layer specific for the provided RenderNode. The layer
+     * attached to the node will be specific to the RenderPipeline used by this
+     * context
+     *
+     *  @return true if the layer has been created or updated
+     */
+    bool createOrUpdateLayer(RenderNode* node, const DamageAccumulator& dmgAccumulator) {
+        return mRenderPipeline->createOrUpdateLayer(node, dmgAccumulator);
+    }
+
+    /**
+     * Destroy any layers that have been attached to the provided RenderNode removing
+     * any state that may have been set during createOrUpdateLayer().
+     */
+    static void destroyLayer(RenderNode* node);
+
     // Won't take effect until next EGLSurface creation
     void setSwapBehavior(SwapBehavior swapBehavior);
 
diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h
index 3250fed..97cdf7f 100644
--- a/libs/hwui/renderthread/IRenderPipeline.h
+++ b/libs/hwui/renderthread/IRenderPipeline.h
@@ -67,6 +67,8 @@
             LayerUpdateQueue* layerUpdateQueue, bool opaque,
             const BakedOpRenderer::LightInfo& lightInfo) = 0;
     virtual TaskManager* getTaskManager() = 0;
+    virtual bool createOrUpdateLayer(RenderNode* node,
+            const DamageAccumulator& damageAccumulator) = 0;
 
     virtual ~IRenderPipeline() {}
 };
diff --git a/libs/hwui/renderthread/OpenGLPipeline.cpp b/libs/hwui/renderthread/OpenGLPipeline.cpp
index c8971f8..c758f6c 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.cpp
+++ b/libs/hwui/renderthread/OpenGLPipeline.cpp
@@ -183,6 +183,46 @@
     return &Caches::getInstance().tasks;
 }
 
+static bool layerMatchesWH(OffscreenBuffer* layer, int width, int height) {
+    return layer->viewportWidth == (uint32_t)width && layer->viewportHeight == (uint32_t)height;
+}
+
+bool OpenGLPipeline::createOrUpdateLayer(RenderNode* node,
+        const DamageAccumulator& damageAccumulator) {
+    RenderState& renderState = mRenderThread.renderState();
+    OffscreenBufferPool& layerPool = renderState.layerPool();
+    bool transformUpdateNeeded = false;
+    if (node->getLayer() == nullptr) {
+        node->setLayer(layerPool.get(renderState, node->getWidth(), node->getHeight()));
+        transformUpdateNeeded = true;
+    } else if (!layerMatchesWH(node->getLayer(), node->getWidth(), node->getHeight())) {
+        // TODO: remove now irrelevant, currently enqueued damage (respecting damage ordering)
+        // Or, ideally, maintain damage between frames on node/layer so ordering is always correct
+        if (node->properties().fitsOnLayer()) {
+            node->setLayer(layerPool.resize(node->getLayer(), node->getWidth(), node->getHeight()));
+        } else {
+            destroyLayer(node);
+        }
+        transformUpdateNeeded = true;
+    }
+
+    if (transformUpdateNeeded && node->getLayer()) {
+        // update the transform in window of the layer to reset its origin wrt light source position
+        Matrix4 windowTransform;
+        damageAccumulator.computeCurrentTransform(&windowTransform);
+        node->getLayer()->setWindowTransform(windowTransform);
+    }
+
+    return transformUpdateNeeded;
+}
+
+void OpenGLPipeline::destroyLayer(RenderNode* node) {
+    if (OffscreenBuffer* layer = node->getLayer()) {
+        layer->renderState.layerPool().putOrDelete(layer);
+        node->setLayer(nullptr);
+    }
+}
+
 } /* namespace renderthread */
 } /* namespace uirenderer */
 } /* namespace android */
diff --git a/libs/hwui/renderthread/OpenGLPipeline.h b/libs/hwui/renderthread/OpenGLPipeline.h
index e08fd9b..34d9bc0 100644
--- a/libs/hwui/renderthread/OpenGLPipeline.h
+++ b/libs/hwui/renderthread/OpenGLPipeline.h
@@ -56,6 +56,9 @@
             LayerUpdateQueue* layerUpdateQueue, bool opaque,
             const BakedOpRenderer::LightInfo& lightInfo) override;
     TaskManager* getTaskManager() override;
+    bool createOrUpdateLayer(RenderNode* node,
+            const DamageAccumulator& damageAccumulator) override;
+    static void destroyLayer(RenderNode* node);
 
 private:
     EglManager& mEglManager;
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index abbace1..bd68fec 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -389,8 +389,8 @@
         /** @hide Stream over a socket, limited to a single stream */
         public static final int OUTPUT_FORMAT_RTP_AVP = 7;
 
-        /** @hide H.264/AAC data encapsulated in MPEG2/TS */
-        public static final int OUTPUT_FORMAT_MPEG2TS = 8;
+        /** H.264/AAC data encapsulated in MPEG2/TS */
+        public static final int MPEG_2_TS = 8;
 
         /** VP8/VORBIS data in a WEBM container */
         public static final int WEBM = 9;
diff --git a/media/java/android/media/midi/MidiDeviceInfo.java b/media/java/android/media/midi/MidiDeviceInfo.java
index 66d1ed7..5fd9006 100644
--- a/media/java/android/media/midi/MidiDeviceInfo.java
+++ b/media/java/android/media/midi/MidiDeviceInfo.java
@@ -20,6 +20,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import android.util.Log;
+
 /**
  * This class contains information to describe a MIDI device.
  * For now we only have information that can be retrieved easily for USB devices,
@@ -352,9 +354,15 @@
     private Bundle getBasicProperties(String[] keys) {
         Bundle basicProperties = new Bundle();
         for (String key : keys) {
-            String val = mProperties.getString(key);
+            Object val = mProperties.get(key);
             if (val != null) {
-                basicProperties.putString(key, val);
+                if (val instanceof String) {
+                    basicProperties.putString(key, (String) val);
+                } else if (val instanceof Integer) {
+                    basicProperties.putInt(key, (Integer) val);
+                } else {
+                    Log.w(TAG, "Unsupported property type: " + val.getClass().getName());
+                }
             }
         }
         return basicProperties;
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 810996e..c2c66fd 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -32,6 +32,7 @@
 #include <gui/Surface.h>
 
 #include <media/ICrypto.h>
+#include <media/MediaCodecBuffer.h>
 #include <media/stagefright/MediaCodec.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
@@ -407,7 +408,7 @@
 
 status_t JMediaCodec::getBuffers(
         JNIEnv *env, bool input, jobjectArray *bufArray) const {
-    Vector<sp<ABuffer> > buffers;
+    Vector<sp<MediaCodecBuffer> > buffers;
 
     status_t err =
         input
@@ -425,7 +426,7 @@
     }
 
     for (size_t i = 0; i < buffers.size(); ++i) {
-        const sp<ABuffer> &buffer = buffers.itemAt(i);
+        const sp<MediaCodecBuffer> &buffer = buffers.itemAt(i);
 
         jobject byteBuffer = NULL;
         err = createByteBufferFromABuffer(
@@ -446,8 +447,9 @@
 }
 
 // static
+template <typename T>
 status_t JMediaCodec::createByteBufferFromABuffer(
-        JNIEnv *env, bool readOnly, bool clearBuffer, const sp<ABuffer> &buffer,
+        JNIEnv *env, bool readOnly, bool clearBuffer, const sp<T> &buffer,
         jobject *buf) const {
     // if this is an ABuffer that doesn't actually hold any accessible memory,
     // use a null ByteBuffer
@@ -492,7 +494,7 @@
 
 status_t JMediaCodec::getBuffer(
         JNIEnv *env, bool input, size_t index, jobject *buf) const {
-    sp<ABuffer> buffer;
+    sp<MediaCodecBuffer> buffer;
 
     status_t err =
         input
@@ -509,7 +511,7 @@
 
 status_t JMediaCodec::getImage(
         JNIEnv *env, bool input, size_t index, jobject *buf) const {
-    sp<ABuffer> buffer;
+    sp<MediaCodecBuffer> buffer;
 
     status_t err =
         input
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index c0c47ef..5f0d3df 100644
--- a/media/jni/android_media_MediaCodec.h
+++ b/media/jni/android_media_MediaCodec.h
@@ -31,7 +31,7 @@
 struct AMessage;
 struct AString;
 struct ICrypto;
-struct IGraphicBufferProducer;
+class IGraphicBufferProducer;
 struct MediaCodec;
 struct PersistentSurface;
 class Surface;
@@ -146,8 +146,9 @@
 
     status_t mInitStatus;
 
+    template <typename T>
     status_t createByteBufferFromABuffer(
-            JNIEnv *env, bool readOnly, bool clearBuffer, const sp<ABuffer> &buffer,
+            JNIEnv *env, bool readOnly, bool clearBuffer, const sp<T> &buffer,
             jobject *buf) const;
 
     void cacheJavaObjects(JNIEnv *env);
diff --git a/packages/Keyguard/res/values-da/strings.xml b/packages/Keyguard/res/values-da/strings.xml
index ebea6ee..b98a253 100644
--- a/packages/Keyguard/res/values-da/strings.xml
+++ b/packages/Keyguard/res/values-da/strings.xml
@@ -128,5 +128,5 @@
       <item quantity="one">Enheden blev sidst låst op for <xliff:g id="NUMBER_1">%d</xliff:g> timer siden. Bekræft adgangskoden.</item>
       <item quantity="other">Enheden blev sidst låst op for <xliff:g id="NUMBER_1">%d</xliff:g> timer siden. Bekræft adgangskoden.</item>
     </plurals>
-    <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Kan ikke genkendes"</string>
+    <string name="fingerprint_not_recognized" msgid="2690661881608146617">"Ikke genkendt"</string>
 </resources>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
index f688a8e..23c6615 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PrintActivity.java
@@ -46,7 +46,6 @@
 import android.os.IBinder;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.os.UserManager;
 import android.print.IPrintDocumentAdapter;
 import android.print.PageRange;
@@ -760,6 +759,13 @@
                 mPrintJob.setPrinterId(printerInfo.getId());
                 mPrintJob.setPrinterName(printerInfo.getName());
 
+                if (canPrint(printerInfo)) {
+                    updatePrintAttributesFromCapabilities(printerInfo.getCapabilities());
+                    onPrinterAvailable(printerInfo);
+                } else {
+                    onPrinterUnavailable(printerInfo);
+                }
+
                 mDestinationSpinnerAdapter.ensurePrinterInVisibleAdapterPosition(printerInfo);
 
                 MetricsLogger.action(this, MetricsEvent.ACTION_PRINTER_SELECT_ALL,
@@ -2046,7 +2052,7 @@
     }
 
     public void onPrinterUnavailable(PrinterInfo printer) {
-        if (mCurrentPrinter.getId().equals(printer.getId())) {
+        if (mCurrentPrinter == null || mCurrentPrinter.getId().equals(printer.getId())) {
             setState(STATE_PRINTER_UNAVAILABLE);
             mPrintedDocument.cancel(false);
             ensureErrorUiShown(getString(R.string.print_error_printer_unavailable),
@@ -2305,8 +2311,7 @@
         public int getPrinterIndex(PrinterId printerId) {
             for (int i = 0; i < getCount(); i++) {
                 PrinterHolder printerHolder = (PrinterHolder) getItem(i);
-                if (printerHolder != null && !printerHolder.removed
-                        && printerHolder.printer.getId().equals(printerId)) {
+                if (printerHolder != null && printerHolder.printer.getId().equals(printerId)) {
                     return i;
                 }
             }
@@ -2535,7 +2540,11 @@
                 if (updatedPrinter != null) {
                     printerHolder.printer = updatedPrinter;
                     printerHolder.removed = false;
-                    onPrinterAvailable(printerHolder.printer);
+                    if (canPrint(printerHolder.printer)) {
+                        onPrinterAvailable(printerHolder.printer);
+                    } else {
+                        onPrinterUnavailable(printerHolder.printer);
+                    }
                     newPrinterHolders.add(printerHolder);
                 } else if (mCurrentPrinter != null && mCurrentPrinter.getId().equals(oldPrinterId)){
                     printerHolder.removed = true;
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 81af941..20e3f6f 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi-verbinding het misluk"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Stawingsprobleem"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Nie binne ontvangs nie"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Sal nie outomaties koppel nie"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Geen internettoegang nie"</string>
     <string name="saved_network" msgid="4352716707126620811">"Gestoor deur <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Gekoppel via Wi-Fi-assistent"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Gekoppel via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 792c434..e4455a4 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"የWiFi ግንኙነት መሰናከል"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"የማረጋገጫ ችግር"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"በክልል ውስጥ የለም"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"በራስ-ሰር አይገናኝም"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"ምንም የበይነመረብ መዳረሻ ያለም"</string>
     <string name="saved_network" msgid="4352716707126620811">"የተቀመጠው በ<xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"በWi‑Fi ረዳት አማካኝነት ተገናኝቷል"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"በ%1$s በኩል መገናኘት"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index db9afee..24b51ec 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"‏أخفق اتصال WiFi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"حدثت مشكلة في المصادقة"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"ليست في النطاق"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"لن يتم الاتصال تلقائيًا"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"لا يتوفر اتصال بالإنترنت"</string>
     <string name="saved_network" msgid="4352716707126620811">"تم الحفظ بواسطة <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"‏تم التوصيل عبر مساعد Wi-Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"‏تم الاتصال عبر %1$s"</string>
diff --git a/packages/SettingsLib/res/values-az-rAZ/strings.xml b/packages/SettingsLib/res/values-az-rAZ/strings.xml
index 130cd75..5eb7a00 100644
--- a/packages/SettingsLib/res/values-az-rAZ/strings.xml
+++ b/packages/SettingsLib/res/values-az-rAZ/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi Bağlantı Uğursuzluğu"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentifikasiya problemi"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Diapazonda deyil"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Avtomatik qoşulmayacaq"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"İnternet girişi yoxdur"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> tərəfindən saxlandı"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi köməkçisi vasitəsilə qoşulub"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s vasitəsilə qoşuludur"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 5d7e852..1acc445 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi veza je otkazala"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem sa potvrdom autentičnosti"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Nije u opsegu"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Automatsko povezivanje nije uspelo"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Nema pristupa internetu"</string>
     <string name="saved_network" msgid="4352716707126620811">"Sačuvao/la je <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Povezano preko Wi‑Fi pomoćnika"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Veza je uspostavljena preko pristupne tačke %1$s"</string>
diff --git a/packages/SettingsLib/res/values-be-rBY/strings.xml b/packages/SettingsLib/res/values-be-rBY/strings.xml
index a8428f2..0e3faca 100644
--- a/packages/SettingsLib/res/values-be-rBY/strings.xml
+++ b/packages/SettingsLib/res/values-be-rBY/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Збой падлучэння Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Праблема аўтэнтыфікацыі"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Не ў зоне дасягальнасці"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Не будзе аўтаматычна падключацца"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Няма доступу да інтэрнэту"</string>
     <string name="saved_network" msgid="4352716707126620811">"Хто захаваў: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Падлучана праз памочніка Wi‑Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Падлучана праз %1$s"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index b356766..1d618d7 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Неуспешна връзка с Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Проблем при удостоверяването"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Извън обхват"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Няма да се свърже автоматично"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Няма достъп до интернет"</string>
     <string name="saved_network" msgid="4352716707126620811">"Запазено от <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Установена е връзка чрез помощника за Wi-Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Установена е връзка през „%1$s“"</string>
diff --git a/packages/SettingsLib/res/values-bn-rBD/strings.xml b/packages/SettingsLib/res/values-bn-rBD/strings.xml
index 4089562..c094d27 100644
--- a/packages/SettingsLib/res/values-bn-rBD/strings.xml
+++ b/packages/SettingsLib/res/values-bn-rBD/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi সংযোগের ব্যর্থতা"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"প্রমাণীকরণ সমস্যা"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"পরিসরের মধ্যে নয়"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"স্বয়ংক্রিয়ভাবে সংযোগ করবে না"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"কোনো ইন্টারনেট অ্যাক্সেস নেই"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> দ্বারা সংরক্ষিত"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"ওয়াই-ফাই সহায়ক-এর মাধ্যমে সংযুক্ত হয়েছে"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s মাধ্যমে সংযুক্ত হয়েছে"</string>
diff --git a/packages/SettingsLib/res/values-bs-rBA/strings.xml b/packages/SettingsLib/res/values-bs-rBA/strings.xml
index 5115bbd..b7a7d29 100644
--- a/packages/SettingsLib/res/values-bs-rBA/strings.xml
+++ b/packages/SettingsLib/res/values-bs-rBA/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Greška pri povezivanju na Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem pri provjeri vjerodostojnosti."</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Nije u dometu"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Neće se automatski povezati"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Nema pristupa internetu"</string>
     <string name="saved_network" msgid="4352716707126620811">"Sačuvao <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Povezani preko Wi-Fi pomoćnika"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Povezani preko %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index abc9390..0d696d5 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Error de connexió Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema d\'autenticació"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Fora de l\'abast"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"No es connectarà automàticament"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"No hi ha accés a Internet"</string>
     <string name="saved_network" msgid="4352716707126620811">"Desat per <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Connectat mitjançant l\'assistent de Wi‑Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Connectada mitjançant %1$s"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index edc53ed..486819a 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Selhání připojení Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problém s ověřením"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Mimo dosah"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Připojení nebude automaticky navázáno"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Nebyl zjištěn žádný přístup k internetu"</string>
     <string name="saved_network" msgid="4352716707126620811">"Uloženo uživatelem <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Připojeno pomocí asistenta připojení Wi-Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Připojeno prostřednictvím %1$s"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 5714901..af4bf05 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi-forbindelsesfejl"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem med godkendelse"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Ikke inden for rækkevidde"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Der oprettes ikke automatisk forbindelse"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Ingen internetadgang"</string>
     <string name="saved_network" msgid="4352716707126620811">"Gemt af <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Forbindelse via Wi-Fi-assistent"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Tilsluttet via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 406523e..e4da20d 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WLAN-Verbindungsfehler"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Authentifizierungsproblem"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Nicht in Reichweite"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Kein automatischer Verbindungsaufbau"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Kein Internetzugriff"</string>
     <string name="saved_network" msgid="4352716707126620811">"Gespeichert von <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Über WLAN-Assistenten verbunden"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Über %1$s verbunden"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 117b9fe..715b6c4 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Αποτυχία σύνδεσης Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Πρόβλημα ελέγχου ταυτότητας"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Εκτός εμβέλειας"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Δεν θα συνδεθεί αυτόματα"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Δεν υπάρχει πρόσβαση στο διαδίκτυο"</string>
     <string name="saved_network" msgid="4352716707126620811">"Αποθηκεύτηκε από <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Σύνδεση μέσω βοηθού Wi‑Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Συνδέθηκε μέσω %1$s"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 35ec15b..f3bc540 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi Connection Failure"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Authentication problem"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Not in range"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Won\'t automatically connect"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"No Internet access"</string>
     <string name="saved_network" msgid="4352716707126620811">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Connected via Wi‑Fi assistant"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Connected via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 35ec15b..f3bc540 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi Connection Failure"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Authentication problem"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Not in range"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Won\'t automatically connect"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"No Internet access"</string>
     <string name="saved_network" msgid="4352716707126620811">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Connected via Wi‑Fi assistant"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Connected via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 35ec15b..f3bc540 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi Connection Failure"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Authentication problem"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Not in range"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Won\'t automatically connect"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"No Internet access"</string>
     <string name="saved_network" msgid="4352716707126620811">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Connected via Wi‑Fi assistant"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Connected via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 2874e61..8f91cb7 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Error de conexión Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema de autenticación"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Fuera de alcance"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"No se conectará automáticamente"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"No se detectó acceso a Internet"</string>
     <string name="saved_network" msgid="4352716707126620811">"Guardadas por <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Conexión por asistente de Wi-Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Conexión a través de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 7d0097a..fc8da79 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Error de conexión Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Error de autenticación"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Fuera de rango"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"No se establecerá conexión automáticamente"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"No se ha detectado acceso a Internet"</string>
     <string name="saved_network" msgid="4352716707126620811">"Guardadas por <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Conectado a través de asistente Wi‑Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Conectado a través de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-et-rEE/strings.xml b/packages/SettingsLib/res/values-et-rEE/strings.xml
index 97fe73f..d28818b 100644
--- a/packages/SettingsLib/res/values-et-rEE/strings.xml
+++ b/packages/SettingsLib/res/values-et-rEE/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi-ühenduse viga"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentimise probleem"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Pole vahemikus"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Automaatselt ei ühendata"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Interneti-ühendus puudub"</string>
     <string name="saved_network" msgid="4352716707126620811">"Salvestas: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Ühendatud WiFi-abi kaudu"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Ühendatud üksuse %1$s kaudu"</string>
diff --git a/packages/SettingsLib/res/values-eu-rES/strings.xml b/packages/SettingsLib/res/values-eu-rES/strings.xml
index 9234f24..a975971 100644
--- a/packages/SettingsLib/res/values-eu-rES/strings.xml
+++ b/packages/SettingsLib/res/values-eu-rES/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Ezin izan da konektatu Wi-Fi sarera"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentifikazio-arazoa"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Urrunegi"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Ez da konektatuko automatikoki"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Ezin da konektatu Internetera"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> aplikazioak gorde du"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi laguntzailearen bidez konektatuta"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s bidez konektatuta"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index bb2e166..8f613aa 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"‏اتصال Wi-Fi برقرار نشد"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"مشکل احراز هویت"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"در محدوده نیست"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"اتصال به‌صورت خودکار انجام نمی‌شود"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"دسترسی به اینترنت وجود ندارد"</string>
     <string name="saved_network" msgid="4352716707126620811">"ذخیره‌شده توسط <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"‏متصل شده از طریق دستیار Wi-Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"‏متصل از طریق %1$s"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index c04c970..ea2a17f 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi-yhteysvirhe"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Todennusvirhe"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Ei kantoalueella"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Yhteyttä ei muodosteta automaattisesti"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Ei internetyhteyttä"</string>
     <string name="saved_network" msgid="4352716707126620811">"Tallentaja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Yhteys muodostettu Wi‑Fi-apurin kautta"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Yhdistetty seuraavan kautta: %1$s"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 1b34904..3001a69 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Échec de connexion Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problème d\'authentification"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Hors de portée"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Reconnexion automatique impossible"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Aucun accès à Internet"</string>
     <string name="saved_network" msgid="4352716707126620811">"Enregistrés par <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Connecté à l\'aide de l\'assistant Wi-Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Connecté par %1$s"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 07398f3..c00a030 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Échec de la connexion Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problème d\'authentification."</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Hors de portée"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Reconnexion automatique impossible"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Aucun accès à Internet"</string>
     <string name="saved_network" msgid="4352716707126620811">"Enregistré par <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Connecté via l\'assistant Wi‑Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Connecté via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-gl-rES/strings.xml b/packages/SettingsLib/res/values-gl-rES/strings.xml
index 5207a73..03cf223 100644
--- a/packages/SettingsLib/res/values-gl-rES/strings.xml
+++ b/packages/SettingsLib/res/values-gl-rES/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Erro na conexión wifi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema de autenticación"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Non está dentro da zona de cobertura"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Non se conectará automaticamente"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Non hai acceso a Internet"</string>
     <string name="saved_network" msgid="4352716707126620811">"Redes gardadas por <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Conectado ao asistente de wifi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Conectado a través de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-gu-rIN/strings.xml b/packages/SettingsLib/res/values-gu-rIN/strings.xml
index 3d0a02b..737af54 100644
--- a/packages/SettingsLib/res/values-gu-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-gu-rIN/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi કનેક્શન નિષ્ફળ"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"પ્રમાણીકરણ સમસ્યા"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"રેન્જમાં નથી"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"આપમેળે કનેક્ટ કરશે નહીં"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"કોઈ ઇન્ટરનેટ ઍક્સેસ નથી"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા સચવાયું"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Wi-Fi સહાયક દ્વારા કનેક્ટ થયું"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s દ્વારા કનેક્ટ થયેલ"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 329a670..efefca3 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"वाईफ़ाई कनेक्‍शन विफलता"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"प्रमाणीकरण समस्या"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"रेंज में नहीं"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"अपने आप कनेक्ट नहीं होगा"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"कोई इंटरनेट एक्सेस नहीं"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> के द्वारा सहेजा गया"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"वाई-फ़ाई सहायक के द्वारा कनेक्‍ट है"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s के द्वारा उपलब्ध"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 89b0653..8990536 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Povezivanje s Wi-Fi-jem nije uspjelo"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem u autentifikaciji"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Nije u rasponu"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Neće se povezati automatski"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Nema pristupa internetu"</string>
     <string name="saved_network" msgid="4352716707126620811">"Spremljeno: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Povezani putem pomoćnika za Wi-Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Povezano putem %1$s"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index c33943a..c557161 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi-kapcsolati hiba"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Azonosítási probléma"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Hatókörön kívül"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Nem csatlakozik automatikusan"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Nincs internet-hozzáférés"</string>
     <string name="saved_network" msgid="4352716707126620811">"Mentette: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Csatlakozva Wi‑Fi-segéddel"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Csatlakozva a következőn keresztül: %1$s"</string>
diff --git a/packages/SettingsLib/res/values-hy-rAM/strings.xml b/packages/SettingsLib/res/values-hy-rAM/strings.xml
index 32b0dd6..41556dc 100644
--- a/packages/SettingsLib/res/values-hy-rAM/strings.xml
+++ b/packages/SettingsLib/res/values-hy-rAM/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi կապի ձախողում"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Նույնականացման խնդիր"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Ընդգրկույթից դուրս է"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Ավտոմատ միացում չի կատարվի"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Ինտերնետ կապ չկա"</string>
     <string name="saved_network" msgid="4352716707126620811">"Պահել է հետևյալ օգտվողը՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Կապակցված է Wi‑Fi Օգնականի միջոցով"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Կապակցված է %1$s-ի միջոցով"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 33df1fe..039457d 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Kegagalan Sambungan Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Masalah autentikasi"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Tidak dalam jangkauan"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Tidak akan tersambung otomatis"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Tidak ada akses internet"</string>
     <string name="saved_network" msgid="4352716707126620811">"Disimpan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Terhubung melalui Asisten Wi-Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Terhubung melalui %1$s"</string>
diff --git a/packages/SettingsLib/res/values-is-rIS/strings.xml b/packages/SettingsLib/res/values-is-rIS/strings.xml
index 8cc408f..a3126bf 100644
--- a/packages/SettingsLib/res/values-is-rIS/strings.xml
+++ b/packages/SettingsLib/res/values-is-rIS/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi-tengingarvilla"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Vandamál við auðkenningu"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Ekkert samband"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Mun ekki tengjast sjálfkrafa"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Enginn netaðgangur"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> vistaði"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Tengt í gegnum Wi-Fi aðstoð"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Tengt í gegnum %1$s"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 0bb26e1..bff3bb7 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Errore connessione Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema di autenticazione"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Fuori portata"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Non verrà eseguita la connessione automatica"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Nessun accesso a Internet"</string>
     <string name="saved_network" msgid="4352716707126620811">"Salvata da <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Connesso tramite assistente Wi‑Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Collegato tramite %1$s"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 7510911..4d794fc 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"‏כשל בחיבור Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"בעיית אימות"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"מחוץ לטווח"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"לא יתבצע חיבור באופן אוטומטי"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"אין גישה לאינטרנט"</string>
     <string name="saved_network" msgid="4352716707126620811">"נשמר על ידי <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"‏מחובר באמצעות אסיסטנט ה-Wi-Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"‏מחובר דרך %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index f657c1e..4c4eed0 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -28,8 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi接続エラー"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"認証に問題"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"圏外"</string>
-    <string name="wifi_no_internet_no_reconnect" msgid="2211781637653149657">"インターネット アクセスを検出できないため、自動的に再接続されません。"</string>
-    <string name="wifi_no_internet" msgid="5011955173375805204">"インターネットに接続していません。"</string>
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"自動的に接続されません"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"インターネットに接続していません"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g>で保存"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fiアシスタント経由で接続"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s経由で接続"</string>
diff --git a/packages/SettingsLib/res/values-ka-rGE/strings.xml b/packages/SettingsLib/res/values-ka-rGE/strings.xml
index f349c4f..2142fa3 100644
--- a/packages/SettingsLib/res/values-ka-rGE/strings.xml
+++ b/packages/SettingsLib/res/values-ka-rGE/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi კავშირის შეფერხება"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ავთენტიკაციის პრობლემა"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"არ არის დიაპაზონში"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"ავტომატურად დაკავშირება ვერ მოხერხდება"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"ინტერნეტთან კავშირი არ არის"</string>
     <string name="saved_network" msgid="4352716707126620811">"შენახული <xliff:g id="NAME">%1$s</xliff:g>-ის მიერ"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"დაკავშირებულია Wi-Fi თანაშემწით"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s-ით დაკავშირებული"</string>
diff --git a/packages/SettingsLib/res/values-kk-rKZ/strings.xml b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
index 1141fca..e00e820 100644
--- a/packages/SettingsLib/res/values-kk-rKZ/strings.xml
+++ b/packages/SettingsLib/res/values-kk-rKZ/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi байланысының қатесі"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Растау мәселесі"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Аумақта жоқ"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Автоматты қосылмайды"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Интернетпен байланыс жоқ"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> сақтаған"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi көмекшісі арқылы қосылу орындалды"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s арқылы қосылған"</string>
diff --git a/packages/SettingsLib/res/values-km-rKH/strings.xml b/packages/SettingsLib/res/values-km-rKH/strings.xml
index 7d1c797..3593c4a 100644
--- a/packages/SettingsLib/res/values-km-rKH/strings.xml
+++ b/packages/SettingsLib/res/values-km-rKH/strings.xml
@@ -28,9 +28,9 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"ការ​ភ្ជាប់​ WiFi បរាជ័យ"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"បញ្ហា​ក្នុង​ការ​ផ្ទៀងផ្ទាត់"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"នៅ​ក្រៅ​តំបន់"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
+    <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
     <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
+    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
     <skip />
     <string name="saved_network" msgid="4352716707126620811">"បានរក្សាទុកដោយ <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"បានភ្ជាប់តាមរយៈជំនួយការ Wi‑Fi"</string>
diff --git a/packages/SettingsLib/res/values-kn-rIN/strings.xml b/packages/SettingsLib/res/values-kn-rIN/strings.xml
index 9039e25..312beae 100644
--- a/packages/SettingsLib/res/values-kn-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-kn-rIN/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi ಸಂಪರ್ಕ ವಿಫಲತೆ"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ಪ್ರಮಾಣೀಕರಣ ಸಮಸ್ಯೆ"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"ವ್ಯಾಪ್ತಿಯಲ್ಲಿಲ್ಲ"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವಿಲ್ಲ"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> ರಿಂದ ಉಳಿಸಲಾಗಿದೆ"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi ಸಹಾಯಕದ ಮೂಲಕ ಸಂಪರ್ಕಿತಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s ಮೂಲಕ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index e33b989..72b5fe3 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi 연결 실패"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"인증 문제"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"범위 내에 없음"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"자동으로 연결되지 않습니다."</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"인터넷에 연결되어 있지 않습니다."</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g>(으)로 저장됨"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi 도우미를 통해 연결됨"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s을(를) 통해 연결됨"</string>
diff --git a/packages/SettingsLib/res/values-ky-rKG/strings.xml b/packages/SettingsLib/res/values-ky-rKG/strings.xml
index 22622dd..a5386c8 100644
--- a/packages/SettingsLib/res/values-ky-rKG/strings.xml
+++ b/packages/SettingsLib/res/values-ky-rKG/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi туташуусу бузулду"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Аутентификация маселеси бар"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Тейлөө аймагында эмес"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Автоматтык түрдө туташпайт"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Интернетке туташпай турат"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> тарабынан сакталды"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi жардамчысы аркылуу туташып турат"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s аркылуу жеткиликтүү"</string>
diff --git a/packages/SettingsLib/res/values-lo-rLA/strings.xml b/packages/SettingsLib/res/values-lo-rLA/strings.xml
index 716ee6f..2dc4fd7 100644
--- a/packages/SettingsLib/res/values-lo-rLA/strings.xml
+++ b/packages/SettingsLib/res/values-lo-rLA/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"​ການ​ເຊື່ອມ​ຕໍ່ WiFi ລົ້ມ​ເຫຼວ"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ບັນຫາການພິສູດຢືນຢັນ"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"ບໍ່ຢູ່ໃນໄລຍະທີ່ເຊື່ອມຕໍ່ໄດ້"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"ຈະບໍ່ເຊື່ອມຕໍ່ອັດຕະໂນມັດ"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
     <string name="saved_network" msgid="4352716707126620811">"ບັນທຶກ​​​ໂດຍ <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"ເຊື່ອມ​ຕໍ່​ຜ່ານ Wi‑Fi ຕົວ​ຊ່ວຍ​ແລ້ວ"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"​ເຊື່ອມຕໍ່​ຜ່ານ %1$s ​ແລ້ວ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 167e756..51fb7fa 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"„Wi-Fi“ ryšio triktis"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentifikavimo problema"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Ne diapazone"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Nebus automatiškai prisijungiama"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Nėra interneto ryšio"</string>
     <string name="saved_network" msgid="4352716707126620811">"Išsaugojo <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Prisijungta naudojant „Wi‑Fi“ pagelbiklį"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Prisijungta naudojant „%1$s“"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 7f5e111..9d4d055 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi savienojuma kļūme"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentificēšanas problēma"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Nav diapazona ietvaros"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Savienojums netiks izveidots automātiski"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Nav piekļuves internetam"</string>
     <string name="saved_network" msgid="4352716707126620811">"Saglabāja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Izveidots savienojums ar Wi‑Fi palīgu"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Savienots, izmantojot %1$s"</string>
diff --git a/packages/SettingsLib/res/values-mk-rMK/strings.xml b/packages/SettingsLib/res/values-mk-rMK/strings.xml
index 2a454d2..7521250 100644
--- a/packages/SettingsLib/res/values-mk-rMK/strings.xml
+++ b/packages/SettingsLib/res/values-mk-rMK/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Поврзувањето преку Wi-Fi не успеа"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Проблем со автентикација"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Надвор од опсег"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Не може да се поврзе автоматски"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Нема пристап до Интернет"</string>
     <string name="saved_network" msgid="4352716707126620811">"Зачувано од <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Поврзано преку помошник за Wi-Fismile"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Поврзано преку %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ml-rIN/strings.xml b/packages/SettingsLib/res/values-ml-rIN/strings.xml
index 56d1477..fed05c9 100644
--- a/packages/SettingsLib/res/values-ml-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ml-rIN/strings.xml
@@ -28,9 +28,9 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi കണക്ഷൻ പരാജയം"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ആധികാരികമാക്കുന്നതിലെ പ്രശ്‌നം"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"പരിധിയിലില്ല"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
+    <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
     <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
+    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
     <skip />
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> സംരക്ഷിച്ചത്"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"വൈഫൈ അസിസ്റ്റന്റ് മുഖേന കണക്‌റ്റുചെയ്തു"</string>
diff --git a/packages/SettingsLib/res/values-mn-rMN/strings.xml b/packages/SettingsLib/res/values-mn-rMN/strings.xml
index 9848cae..3d32aea 100644
--- a/packages/SettingsLib/res/values-mn-rMN/strings.xml
+++ b/packages/SettingsLib/res/values-mn-rMN/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi холболт амжилтгүй"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Гэрчлэлийн асуудал"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Хүрээнд байхгүй"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Автоматаар холбогдохгүй"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Интернэт холболт алга"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> хадгалсан"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Wi-Fi туслагчаар дамжуулан холбогдлоо"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s-р холбогдсон"</string>
diff --git a/packages/SettingsLib/res/values-mr-rIN/strings.xml b/packages/SettingsLib/res/values-mr-rIN/strings.xml
index 83ae663..e81b5ae 100644
--- a/packages/SettingsLib/res/values-mr-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-mr-rIN/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi कनेक्शन अयशस्वी"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"प्रमाणीकरण समस्या"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"परिक्षेत्रामध्ये नाही"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"स्वयंचलितपणे कनेक्ट करणार नाही"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"इंटरनेट प्रवेश नाही"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> द्वारे जतन केले"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi सहाय्यक द्वारे कनेक्ट केले"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s द्वारे कनेक्‍ट केले"</string>
diff --git a/packages/SettingsLib/res/values-ms-rMY/strings.xml b/packages/SettingsLib/res/values-ms-rMY/strings.xml
index 217d82a..2368d09 100644
--- a/packages/SettingsLib/res/values-ms-rMY/strings.xml
+++ b/packages/SettingsLib/res/values-ms-rMY/strings.xml
@@ -28,9 +28,9 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Kegagalan Sambungan WiFi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Masalah pengesahan"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Tidak dalam liputan"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
+    <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
     <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
+    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
     <skip />
     <string name="saved_network" msgid="4352716707126620811">"Diselamatkan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Disambungkan melalui Pembantu Wi-Fi"</string>
diff --git a/packages/SettingsLib/res/values-my-rMM/strings.xml b/packages/SettingsLib/res/values-my-rMM/strings.xml
index fc977a9..95711a4 100644
--- a/packages/SettingsLib/res/values-my-rMM/strings.xml
+++ b/packages/SettingsLib/res/values-my-rMM/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi ချိတ်ဆက်မှု မအောင်မြင်ပါ"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"စစ်မှန်ကြောင်းအတည်ပြုရန်၌ ပြသနာရှိခြင်း"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"စက်ကွင်းထဲတွင် မဟုတ်ပါ"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"အလိုအလျောက်ချိတ်ဆက်မည်မဟုတ်ပါ"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"အင်တာနက် ချိတ်ဆက်မှု မရှိပါ"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> မှသိမ်းဆည်းခဲ့သည်"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"ကြိုးမဲ့ကူညီသူမှတဆင့် ချိတ်ဆက်၏"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index bd6ae05..1a053e2 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -28,9 +28,9 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi-tilkoblingsfeil"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentiseringsproblem"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Utenfor område"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
+    <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
     <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
+    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
     <skip />
     <string name="saved_network" msgid="4352716707126620811">"Lagret av <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Koblet til via en Wi-Fi-assistent"</string>
diff --git a/packages/SettingsLib/res/values-ne-rNP/strings.xml b/packages/SettingsLib/res/values-ne-rNP/strings.xml
index c671f90..0a7cdab 100644
--- a/packages/SettingsLib/res/values-ne-rNP/strings.xml
+++ b/packages/SettingsLib/res/values-ne-rNP/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"वाईफाई जडान असफल"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"प्रमाणीकरण समस्या"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"दायराभित्र छैन"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"स्वतः जडान हुने छैन"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"इन्टरनेट माथिको पहुँच छैन"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> द्वारा सुरक्षित गरियो"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Wi-Fi सहायक द्वारा जोडिएको"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s मार्फत जडित"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 330eb30..27478a4 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wifi-verbinding mislukt"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Authenticatieprobleem"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Niet binnen bereik"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Er wordt niet automatisch verbinding gemaakt"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Geen internettoegang"</string>
     <string name="saved_network" msgid="4352716707126620811">"Opgeslagen door <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Verbonden via wifi-assistent"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Verbonden via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-pa-rIN/strings.xml b/packages/SettingsLib/res/values-pa-rIN/strings.xml
index 9826023..d06a88f 100644
--- a/packages/SettingsLib/res/values-pa-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-pa-rIN/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi ਕਨੈਕਸ਼ਨ ਅਸਫਲਤਾ"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ਪ੍ਰਮਾਣੀਕਰਨ ਸਮੱਸਿਆ"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"ਰੇਂਜ ਵਿੱਚ ਨਹੀਂ ਹੈ"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"ਕੋਈ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> ਵੱਲੋਂ ਸੁਰੱਖਿਅਤ ਕੀਤਾ"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi ਸਹਾਇਕ ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 4dce1a4..3c09335 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Błąd połączenia Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem z uwierzytelnianiem"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Poza zasięgiem"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Nie można połączyć automatycznie"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Brak dostępu do internetu"</string>
     <string name="saved_network" msgid="4352716707126620811">"Zapisane przez: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Połączono przez Asystenta Wi‑Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Połączono przez %1$s"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 27332f9..1b46c84 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Falha de conexão Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema de autenticação"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Fora do alcance"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Não se conectará automaticamente"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Sem acesso à Internet"</string>
     <string name="saved_network" msgid="4352716707126620811">"Salvas por <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Conectado via assistente de Wi‑Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Conectado via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index c48ccba..c8be0da 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Falha de ligação Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema de autenticação"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Fora do alcance"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Não é efetuada uma ligação automaticamente"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Sem acesso à Internet"</string>
     <string name="saved_network" msgid="4352716707126620811">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Ligado através do Assistente de Wi‑Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Ligado através de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 27332f9..1b46c84 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Falha de conexão Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema de autenticação"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Fora do alcance"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Não se conectará automaticamente"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Sem acesso à Internet"</string>
     <string name="saved_network" msgid="4352716707126620811">"Salvas por <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Conectado via assistente de Wi‑Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Conectado via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 8458557..b022163 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Eroare de conexiune Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problemă la autentificare"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"În afara ariei de acoperire"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Nu se va conecta automat"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Nu există acces la internet"</string>
     <string name="saved_network" msgid="4352716707126620811">"Salvată de <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Conexiune realizată printr-un asistent Wi-Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Conectată prin %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index a89c553..3a4e85b 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Ошибка подключения Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Ошибка аутентификации"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Недоступна"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Подключение не будет выполняться автоматически"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Отсутствует подключение к Интернету"</string>
     <string name="saved_network" msgid="4352716707126620811">"Кто сохранил: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Установлено подключение через Ассистента Wi-Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Подключено к %1$s"</string>
diff --git a/packages/SettingsLib/res/values-si-rLK/strings.xml b/packages/SettingsLib/res/values-si-rLK/strings.xml
index c8a0bad..24779b3 100644
--- a/packages/SettingsLib/res/values-si-rLK/strings.xml
+++ b/packages/SettingsLib/res/values-si-rLK/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi සම්බන්ධතාව අසාර්ථකයි"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"සත්‍යාපනයේ ගැටලුවකි"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"පරාසයේ නැත"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"ස්වයංක්‍රිය නැවත සම්බන්ධ නොවනු ඇත"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"අන්තර්ජාල ප්‍රවේශය නැත"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> විසින් සුරකින ලදී"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi සහායක හරහා සම්බන්ධ කරන ලදි"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s හරහා සම්බන්ධ විය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 958bae6..e8db85b 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Zlyhanie pripojenia Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problém s overením totožnosti"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Mimo dosah"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Nedôjde k automatickému pripojeniu"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Žiadny prístup k internetu"</string>
     <string name="saved_network" msgid="4352716707126620811">"Uložil(a) <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Pripojené pomocou Asistenta Wi-Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Pripojené prostredníctvom %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index d4ad802..d908e6d 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Povezava prek Wi-Fi-ja ni uspela"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Težava s preverjanjem pristnosti"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Ni v obsegu"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Samodejna vnovična vzpostavitev povezave se ne bo izvedla"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Ni dostopa do interneta"</string>
     <string name="saved_network" msgid="4352716707126620811">"Shranil(-a): <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Povezava vzpostavljena prek pomočnika za Wi-Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Vzpostavljena povezava prek: %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sq-rAL/strings.xml b/packages/SettingsLib/res/values-sq-rAL/strings.xml
index ba5d2833..69318a1 100644
--- a/packages/SettingsLib/res/values-sq-rAL/strings.xml
+++ b/packages/SettingsLib/res/values-sq-rAL/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Dështim i lidhjes WiFi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problem me vërtetimin"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Nuk është brenda rrezes"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Nuk do të lidhet automatikisht"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Nuk ka qsaje në internet"</string>
     <string name="saved_network" msgid="4352716707126620811">"E ruajtur nga <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"I lidhur nëpërmjet ndihmësit të Wi‑Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"E lidhur përmes %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index b04a4c2..459d92e 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi веза је отказала"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Проблем са потврдом аутентичности"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Није у опсегу"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Аутоматско повезивање није успело"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Нема приступа интернету"</string>
     <string name="saved_network" msgid="4352716707126620811">"Сачувао/ла је <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Повезано преко Wi‑Fi помоћника"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Веза је успостављена преко приступне тачке %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 7562ee0..d52882a 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi-anslutningsfel"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Autentiseringsproblem"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Utom räckhåll"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Det går inte att ansluta automatiskt"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Ingen internetåtkomst"</string>
     <string name="saved_network" msgid="4352716707126620811">"Sparades av <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Ansluten via Wi-Fi-assistent"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Anslutet via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index 9e64b91..6f2e0cb 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Haikuweza Kuunganisha kwenye WiFi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Tatizo la uthibitishaji"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Haiko karibu"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Haiwezi kuunganisha kiotomatiki"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Hakuna muunganisho wa Intaneti"</string>
     <string name="saved_network" msgid="4352716707126620811">"Ilihifadhiwa na <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Imeunganishwa kupitia Kisaidizi cha Wi-Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Imeunganishwa kupitia %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ta-rIN/strings.xml b/packages/SettingsLib/res/values-ta-rIN/strings.xml
index b9ff6bc..5f218e0 100644
--- a/packages/SettingsLib/res/values-ta-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-ta-rIN/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"வைஃபை இணைப்பில் தோல்வி"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"அங்கீகரிப்புச் சிக்கல்"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"தொடர்பு எல்லையில் இல்லை"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"தானாக இணைக்கப்படாது"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"இணைய அணுகல் இல்லை"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> சேமித்தது"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"வைஃபை அசிஸ்டண்ட் மூலம் இணைக்கப்பட்டது"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s வழியாக இணைக்கப்பட்டது"</string>
diff --git a/packages/SettingsLib/res/values-te-rIN/strings.xml b/packages/SettingsLib/res/values-te-rIN/strings.xml
index 08bcbda..0bd77f7 100644
--- a/packages/SettingsLib/res/values-te-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-te-rIN/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi కనెక్షన్ వైఫల్యం"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ప్రామాణీకరణ సమస్య"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"పరిధిలో లేదు"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"స్వయంచాలకంగా కనెక్ట్ కాదు"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"ఇంటర్నెట్ ప్రాప్యత లేదు"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> ద్వారా సేవ్ చేయబడింది"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi సహాయకం ద్వారా కనెక్ట్ చేయబడింది"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s ద్వారా కనెక్ట్ చేయబడింది"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index bd9d8d5..d8ee2f3 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"การเชื่อมต่อ Wi-Fi ล้มเหลว"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"ปัญหาในการตรวจสอบสิทธิ์"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"ไม่อยู่ในพื้นที่ให้บริการ"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"จะไม่เชื่อมต่อโดยอัตโนมัติ"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"ไม่สามารถเข้าถึงอินเทอร์เน็ต"</string>
     <string name="saved_network" msgid="4352716707126620811">"บันทึกโดย <xliff:g id="NAME">%1$s</xliff:g> แล้ว"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"เชื่อมต่อผ่านตัวช่วย Wi-Fi อยู่"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"เชื่อมต่อผ่าน %1$s แล้ว"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 9c952f5..9b9d26f 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Pagkabigo ng Koneksyon sa WiFi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Problema sa pagpapatotoo"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Wala sa sakop"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Hindi awtomatikong kokonekta"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Walang access sa Internet"</string>
     <string name="saved_network" msgid="4352716707126620811">"Na-save ni <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Nakakonekta sa pamamagitan ng Wi‑Fi assistant"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Nakakonekta sa pamamagitan ng %1$s"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 889b631..643b710 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -28,9 +28,9 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Kablosuz Bağlantı Hatası"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Kimlik doğrulama sorunu"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Kapsama alanı dışında"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
+    <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
     <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
+    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
     <skip />
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> tarafından kaydedildi"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Kablosuz bağlantı yardımcısıyla bağlandı"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index cdf725f..b1fbef5 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Помилка з’єднання Wi-Fi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Проблема з автентифікацією"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Не в діапазоні"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Не під’єднуватиметься автоматично"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Немає доступу до Інтернету"</string>
     <string name="saved_network" msgid="4352716707126620811">"Збережено додатком <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Під’єднано через Диспетчер Wi-Fi-з’єднання"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Під’єднано через %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ur-rPK/strings.xml b/packages/SettingsLib/res/values-ur-rPK/strings.xml
index c5aaaab..322b468 100644
--- a/packages/SettingsLib/res/values-ur-rPK/strings.xml
+++ b/packages/SettingsLib/res/values-ur-rPK/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"‏WiFi کنکشن کی ناکامی"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"توثیق کا مسئلہ"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"رینج میں نہیں ہے"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"خودکار طور پر منسلک نہیں ہو گا"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"انٹرنیٹ تک کوئی رسائی نہیں"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> کی جانب سے محفوظ کردہ"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"‏Wi‑Fi اسسٹنٹ کے ذریعے منسلک ہے"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"‏منسلک بذریعہ ‎%1$s"</string>
diff --git a/packages/SettingsLib/res/values-uz-rUZ/strings.xml b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
index e2c58a4..09ac4b1 100644
--- a/packages/SettingsLib/res/values-uz-rUZ/strings.xml
+++ b/packages/SettingsLib/res/values-uz-rUZ/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Wi-Fi ulanishini o‘rnatib bo‘lmadi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Tasdiqdan o‘tishda muammo"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Aloqada emas"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Avtomatik ravishda ulanilmaydi"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Internet aloqasi yo‘q"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> tomonidan saqlangan"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Wi‑Fi yordamchisi orqali ulangan"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"%1$s orqali ulangan"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index fa4174b..fa41b45 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Lỗi kết nối WiFi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Sự cố xác thực"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Ngoài vùng phủ sóng"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Sẽ không tự động kết nối"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Không có quyền truy cập Internet"</string>
     <string name="saved_network" msgid="4352716707126620811">"Được lưu bởi <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Được kết nối qua trình hỗ trợ Wi‑Fi"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Được kết nối qua %1$s"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 63199e8..2e515da 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -28,9 +28,9 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WLAN 连接失败"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"身份验证出现问题"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"不在范围内"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
+    <!-- no translation found for wifi_no_internet_no_reconnect (5724903347310541706) -->
     <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
+    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
     <skip />
     <string name="saved_network" msgid="4352716707126620811">"已通过<xliff:g id="NAME">%1$s</xliff:g>保存"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"已连接(通过 WLAN 助手)"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index edbffc1..f6c2f93 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi 連線失敗"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"驗證問題"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"超出可用範圍"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"不會自動連線"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"無法偵測互聯網連線"</string>
     <string name="saved_network" msgid="4352716707126620811">"<xliff:g id="NAME">%1$s</xliff:g> 的儲存"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"已透過 Wi-Fi 小幫手連線"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"已透過 %1$s 連線"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index b1c5f06..df9238a 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"WiFi 連線失敗"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"驗證問題"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"不在有效範圍內"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"無法自動連線"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"沒有可用的網際網路連線"</string>
     <string name="saved_network" msgid="4352716707126620811">"由<xliff:g id="NAME">%1$s</xliff:g>儲存"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"已透過 Wi‑Fi 小幫手連線"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"已透過 %1$s 連線"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index e4f0d59..fc9e81f 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -28,10 +28,8 @@
     <string name="wifi_disabled_wifi_failure" msgid="3081668066612876581">"Ukwehlulekla koxhumo le-WiFi"</string>
     <string name="wifi_disabled_password_failure" msgid="8659805351763133575">"Inkinga yokufakazela ubuqiniso"</string>
     <string name="wifi_not_in_range" msgid="1136191511238508967">"Ayikho ebubanzini"</string>
-    <!-- no translation found for wifi_no_internet_no_reconnect (2211781637653149657) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (5011955173375805204) -->
-    <skip />
+    <string name="wifi_no_internet_no_reconnect" msgid="5724903347310541706">"Ngeke ize ixhumeke ngokuzenzakalela"</string>
+    <string name="wifi_no_internet" msgid="3880396223819116454">"Akukho ukufinyelela ku-inthanethi"</string>
     <string name="saved_network" msgid="4352716707126620811">"Kulondolozwe ngu-<xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="connected_via_wfa" msgid="3805736726317410714">"Ixhunywe ngomsizi we-Wi-FI"</string>
     <string name="connected_via_passpoint" msgid="2826205693803088747">"Kuxhumeke nge-%1$s"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 7d9cc53..1a1aa57 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -65,9 +65,9 @@
     <!-- Summary for the remembered network but currently not in range. -->
     <string name="wifi_not_in_range">Not in range</string>
     <!-- Summary for the network but no internet connection was detected. -->
-    <string name="wifi_no_internet_no_reconnect">No Internet Access Detected, won\'t automatically reconnect.</string>
+    <string name="wifi_no_internet_no_reconnect">Won\'t automatically connect</string>
     <!-- Summary for the remembered network but no internet connection was detected. -->
-    <string name="wifi_no_internet">No Internet Access.</string>
+    <string name="wifi_no_internet">No Internet access</string>
     <!-- Summary for saved networks -->
     <string name="saved_network">Saved by <xliff:g id="name">%1$s</xliff:g></string>
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
new file mode 100644
index 0000000..4ec4f4f
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
@@ -0,0 +1,34 @@
+/**
+ * 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.settingslib.drawer;
+
+public final class CategoryKey {
+
+    // Activities in this category shows up in Settings homepage.
+    public static final String CATEGORY_HOMEPAGE = "com.android.settings.category.ia.homepage";
+
+    // Top level category.
+    public static final String CATEGORY_NETWORK = "com.android.settings.category.ia.wireless";
+    public static final String CATEGORY_DEVICE = "com.android.settings.category.ia.device";
+    public static final String CATEGORY_APPS = "com.android.settings.category.ia.apps";
+    public static final String CATEGORY_BATTERY = "com.android.settings.category.ia.battery";
+    public static final String CATEGORY_DISPLAY = "com.android.settings.category.ia.display";
+    public static final String CATEGORY_SOUND = "com.android.settings.category.ia.sound";
+    public static final String CATEGORY_STORAGE = "com.android.settings.category.ia.storage";
+    public static final String CATEGORY_SECURITY = "com.android.settings.category.ia.security";
+    public static final String CATEGORY_ACCOUNT = "com.android.settings.category.ia.accounts";
+    public static final String CATEGORY_SYSTEM = "com.android.settings.category.ia.system";
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
new file mode 100644
index 0000000..ce16336
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
@@ -0,0 +1,116 @@
+/**
+ * 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.settingslib.drawer;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.settingslib.applications.InterestingConfigChanges;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class CategoryManager {
+
+    private static final String TAG = "CategoryManager";
+
+    private static CategoryManager sInstance;
+    private final InterestingConfigChanges mInterestingConfigChanges;
+
+    // Tile cache (key: <packageName, activityName>, value: tile)
+    private final Map<Pair<String, String>, Tile> mTileByComponentCache;
+
+    // Tile cache (key: category key, value: category)
+    private final Map<String, DashboardCategory> mCategoryByKeyMap;
+
+    private List<DashboardCategory> mCategories;
+
+    public static CategoryManager get(Context context) {
+        if (sInstance == null) {
+            sInstance = new CategoryManager(context);
+        }
+        return sInstance;
+    }
+
+    CategoryManager(Context context) {
+        mTileByComponentCache = new ArrayMap<>();
+        mCategoryByKeyMap = new ArrayMap<>();
+        mInterestingConfigChanges = new InterestingConfigChanges();
+        mInterestingConfigChanges.applyNewConfig(context.getResources());
+    }
+
+    public synchronized DashboardCategory getTilesByCategory(Context context, String categoryKey) {
+        tryInitCategories(context);
+
+        final DashboardCategory category = mCategoryByKeyMap.get(categoryKey);
+        if (category == null) {
+            throw new IllegalStateException("Can't find category with key " + categoryKey);
+        }
+        return category;
+    }
+
+    public synchronized List<DashboardCategory> getCategories(Context context) {
+        tryInitCategories(context);
+        return mCategories;
+    }
+
+    public synchronized void reloadAllCategories(Context context) {
+        final boolean forceClearCache = mInterestingConfigChanges.applyNewConfig(
+                context.getResources());
+        mCategories = null;
+        tryInitCategories(context, forceClearCache);
+    }
+
+    public synchronized void updateCategoryFromBlacklist(Set<ComponentName> tileBlacklist) {
+        if (mCategories == null) {
+            Log.w(TAG, "Category is null, skipping blacklist update");
+        }
+        for (int i = 0; i < mCategories.size(); i++) {
+            DashboardCategory category = mCategories.get(i);
+            for (int j = 0; j < category.tiles.size(); j++) {
+                Tile tile = category.tiles.get(j);
+                if (tileBlacklist.contains(tile.intent.getComponent())) {
+                    category.tiles.remove(j--);
+                }
+            }
+        }
+    }
+
+    private synchronized void tryInitCategories(Context context) {
+        // Keep cached tiles by default. The cache is only invalidated when InterestingConfigChange
+        // happens.
+        tryInitCategories(context, false /* forceClearCache */);
+    }
+
+    private synchronized void tryInitCategories(Context context, boolean forceClearCache) {
+        if (mCategories == null) {
+            if (forceClearCache) {
+                mTileByComponentCache.clear();
+            }
+            mCategoryByKeyMap.clear();
+            mCategories = TileUtils.getCategories(context, mTileByComponentCache,
+                    false /* categoryDefinedInManifest */);
+            for (DashboardCategory category : mCategories) {
+                mCategoryByKeyMap.put(category.key, category);
+            }
+        }
+    }
+
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
index 53be0e6..3fc999f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/DashboardCategory.java
@@ -16,15 +16,20 @@
 
 package com.android.settingslib.drawer;
 
+import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
+import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.List;
 
 public class DashboardCategory implements Parcelable {
 
+    private static final String TAG = "DashboardCategory";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
     /**
      * Title of the category that is shown to the user.
      */
@@ -74,6 +79,22 @@
         return tiles.get(n);
     }
 
+    public boolean containsComponent(ComponentName component) {
+        for (Tile tile : tiles) {
+            if (TextUtils.equals(tile.intent.getComponent().getClassName(),
+                    component.getClassName())) {
+                if (DEBUG) {
+                    Log.d(TAG,  "category " + key + "contains component" + component);
+                }
+                return true;
+            }
+        }
+        if (DEBUG) {
+            Log.d(TAG,  "category " + key + " does not contain component" + component);
+        }
+        return false;
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 05585e53e..bad7ba4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -33,7 +33,6 @@
 import android.provider.Settings;
 import android.support.annotation.VisibleForTesting;
 import android.support.v4.widget.DrawerLayout;
-import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.Pair;
@@ -64,12 +63,9 @@
 
     public static final String EXTRA_SHOW_MENU = "show_drawer_menu";
 
-    private static List<DashboardCategory> sDashboardCategories;
-    private static HashMap<Pair<String, String>, Tile> sTileCache;
     // Serves as a temporary list of tiles to ignore until we heard back from the PM that they
     // are disabled.
     private static ArraySet<ComponentName> sTileBlacklist = new ArraySet<>();
-    private static InterestingConfigChanges sConfigTracker;
 
     private final PackageReceiver mPackageReceiver = new PackageReceiver();
     private final List<CategoryListener> mCategoryListeners = new ArrayList<>();
@@ -80,6 +76,15 @@
     private boolean mShowingMenu;
     private UserManager mUserManager;
 
+    // Remove below after new IA
+    @Deprecated
+    private static List<DashboardCategory> sDashboardCategories;
+    @Deprecated
+    private static HashMap<Pair<String, String>, Tile> sTileCache;
+    @Deprecated
+    private static InterestingConfigChanges sConfigTracker;
+    // Remove above after new IA
+
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -105,7 +110,9 @@
             mDrawerLayout = null;
             return;
         }
-        getDashboardCategories();
+        if (!isDashboardFeatureEnabled()) {
+            getDashboardCategories();
+        }
         setActionBar(toolbar);
         mDrawerAdapter = new SettingsDrawerAdapter(this);
         ListView listView = (ListView) findViewById(R.id.left_drawer);
@@ -144,7 +151,11 @@
             filter.addDataScheme("package");
             registerReceiver(mPackageReceiver, filter);
 
-            new CategoriesUpdater().execute();
+            if (isDashboardFeatureEnabled()) {
+                new CategoriesUpdateTask().execute();
+            } else {
+                new CategoriesUpdater().execute();
+            }
         }
         final Intent intent = getIntent();
         if (intent != null) {
@@ -173,23 +184,23 @@
         if (componentName == null) {
             return false;
         }
-        // Look for a tile that has the same component as incoming intent
-        final List<DashboardCategory> categories = getDashboardCategories();
-        for (DashboardCategory category : categories) {
-            for (Tile tile : category.tiles) {
-                if (TextUtils.equals(tile.intent.getComponent().getClassName(),
-                        componentName.getClassName())) {
-                    if (DEBUG) {
-                        Log.d(TAG, "intent is for top level tile: " + tile.title);
-                    }
+        if (isDashboardFeatureEnabled()) {
+            final DashboardCategory homepageCategories = CategoryManager.get(this)
+                    .getTilesByCategory(this, CategoryKey.CATEGORY_HOMEPAGE);
+            return homepageCategories.containsComponent(componentName);
+        } else {
+            // Look for a tile that has the same component as incoming intent
+            final List<DashboardCategory> categories = getDashboardCategories();
+            for (DashboardCategory category : categories) {
+                if (category.containsComponent(componentName)) {
                     return true;
                 }
             }
+            if (DEBUG) {
+                Log.d(TAG, "Intent is not for top level settings " + intent);
+            }
+            return false;
         }
-        if (DEBUG) {
-            Log.d(TAG, "Intent is not for top level settings " + intent);
-        }
-        return false;
     }
 
     public void addCategoryListener(CategoryListener listener) {
@@ -255,7 +266,11 @@
             return;
         }
         // TODO: Do this in the background with some loading.
-        mDrawerAdapter.updateCategories();
+        if (isDashboardFeatureEnabled()) {
+            mDrawerAdapter.updateHomepageCategories();
+        } else {
+            mDrawerAdapter.updateCategories();
+        }
         if (mDrawerAdapter.getCount() != 0) {
             mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
         } else {
@@ -343,13 +358,6 @@
         }
     }
 
-    public HashMap<Pair<String, String>, Tile> getTileCache() {
-        if (sTileCache == null) {
-            getDashboardCategories();
-        }
-        return sTileCache;
-    }
-
     public void onProfileTileOpen() {
         finish();
     }
@@ -368,7 +376,11 @@
                     ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                     : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                     PackageManager.DONT_KILL_APP);
-            new CategoriesUpdater().execute();
+            if (isDashboardFeatureEnabled()) {
+                new CategoriesUpdateTask().execute();
+            } else {
+                new CategoriesUpdater().execute();
+            }
         }
     }
 
@@ -376,6 +388,10 @@
         void onCategoriesChanged();
     }
 
+    /**
+     * @deprecated remove after new IA
+     */
+    @Deprecated
     private class CategoriesUpdater extends AsyncTask<Void, Void, List<DashboardCategory>> {
         @Override
         protected List<DashboardCategory> doInBackground(Void... params) {
@@ -408,10 +424,39 @@
         }
     }
 
+    private class CategoriesUpdateTask extends AsyncTask<Void, Void, Void> {
+
+        private final CategoryManager mCategoryManager;
+
+        public CategoriesUpdateTask() {
+            mCategoryManager = CategoryManager.get(SettingsDrawerActivity.this);
+        }
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            mCategoryManager.reloadAllCategories(SettingsDrawerActivity.this);
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            mCategoryManager.updateCategoryFromBlacklist(sTileBlacklist);
+            onCategoriesChanged();
+        }
+    }
+
+    protected boolean isDashboardFeatureEnabled() {
+        return false;
+    }
+
     private class PackageReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-            new CategoriesUpdater().execute();
+            if (isDashboardFeatureEnabled()) {
+                new CategoriesUpdateTask().execute();
+            } else {
+                new CategoriesUpdater().execute();
+            }
         }
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
index 1d6197a..602d135 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerAdapter.java
@@ -37,6 +37,10 @@
         mActivity = activity;
     }
 
+    /**
+     * @deprecated Remove after new IA
+     */
+    @Deprecated
     void updateCategories() {
         List<DashboardCategory> categories = mActivity.getDashboardCategories();
         mItems.clear();
@@ -64,6 +68,27 @@
         notifyDataSetChanged();
     }
 
+    public void updateHomepageCategories() {
+        final DashboardCategory category = CategoryManager.get(mActivity)
+                        .getTilesByCategory(mActivity, CategoryKey.CATEGORY_HOMEPAGE);
+        mItems.clear();
+        // Spacer.
+        mItems.add(null);
+        Item tile = new Item();
+        tile.label = mActivity.getString(R.string.home);
+        tile.icon = Icon.createWithResource(mActivity, R.drawable.home);
+        mItems.add(tile);
+        for (int j = 0; j < category.tiles.size(); j++) {
+            tile = new Item();
+            Tile dashboardTile = category.tiles.get(j);
+            tile.label = dashboardTile.title;
+            tile.icon = dashboardTile.icon;
+            tile.tile = dashboardTile;
+            mItems.add(tile);
+        }
+        notifyDataSetChanged();
+    }
+
     public Tile getTile(int position) {
         return mItems.get(position) != null ? mItems.get(position).tile : null;
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index e70cc29..b2ce13f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -113,8 +113,24 @@
 
     private static final String SETTING_PKG = "com.android.settings";
 
+    /**
+     * Build a list of DashboardCategory. Each category must be defined in manifest.
+     * eg: .Settings$DeviceSettings
+     * @deprecated
+     */
+    @Deprecated
     public static List<DashboardCategory> getCategories(Context context,
-            HashMap<Pair<String, String>, Tile> cache) {
+            Map<Pair<String, String>, Tile> cache) {
+        return getCategories(context, cache, true /*categoryDefinedInManifest*/);
+    }
+
+    /**
+     * Build a list of DashboardCategory.
+     * @param categoryDefinedInManifest If true, an dummy activity must exists in manifest to
+     * represent this category (eg: .Settings$DeviceSettings)
+     */
+    public static List<DashboardCategory> getCategories(Context context,
+            Map<Pair<String, String>, Tile> cache, boolean categoryDefinedInManifest) {
         final long startTime = System.currentTimeMillis();
         boolean setup = Global.getInt(context.getContentResolver(), Global.DEVICE_PROVISIONED, 0)
                 != 0;
@@ -134,11 +150,12 @@
                 getTilesForAction(context, user, EXTRA_SETTINGS_ACTION, cache, null, tiles, false);
             }
         }
+
         HashMap<String, DashboardCategory> categoryMap = new HashMap<>();
         for (Tile tile : tiles) {
             DashboardCategory category = categoryMap.get(tile.category);
             if (category == null) {
-                category = createCategory(context, tile.category);
+                category = createCategory(context, tile.category, categoryDefinedInManifest);
                 if (category == null) {
                     Log.w(LOG_TAG, "Couldn't find category " + tile.category);
                     continue;
@@ -157,9 +174,21 @@
         return categories;
     }
 
-    private static DashboardCategory createCategory(Context context, String categoryKey) {
+    /**
+     * Create a new DashboardCategory from key.
+     *
+     * @param context Context to query intent
+     * @param categoryKey The category key
+     * @param categoryDefinedInManifest If true, an dummy activity must exists in manifest to
+     * represent this category (eg: .Settings$DeviceSettings)
+     */
+    private static DashboardCategory createCategory(Context context, String categoryKey,
+            boolean categoryDefinedInManifest) {
         DashboardCategory category = new DashboardCategory();
         category.key = categoryKey;
+        if (!categoryDefinedInManifest) {
+            return category;
+        }
         PackageManager pm = context.getPackageManager();
         List<ResolveInfo> results = pm.queryIntentActivities(new Intent(categoryKey), 0);
         if (results.size() == 0) {
@@ -204,6 +233,8 @@
             ActivityInfo activityInfo = resolved.activityInfo;
             Bundle metaData = activityInfo.metaData;
             String categoryKey = defaultCategory;
+
+            // Load category
             if (checkCategory && ((metaData == null) || !metaData.containsKey(EXTRA_CATEGORY_KEY))
                     && categoryKey == null) {
                 Log.w(LOG_TAG, "Found " + resolved.activityInfo.name + " for intent "
@@ -213,6 +244,7 @@
             } else {
                 categoryKey = metaData.getString(EXTRA_CATEGORY_KEY);
             }
+
             Pair<String, String> key = new Pair<String, String>(activityInfo.packageName,
                     activityInfo.name);
             Tile tile = addedCache.get(key);
@@ -238,16 +270,6 @@
         }
     }
 
-    private static DashboardCategory getCategory(List<DashboardCategory> target,
-            String categoryKey) {
-        for (DashboardCategory category : target) {
-            if (categoryKey.equals(category.key)) {
-                return category;
-            }
-        }
-        return null;
-    }
-
     private static boolean updateTileData(Context context, Tile tile,
             ActivityInfo activityInfo, ApplicationInfo applicationInfo, PackageManager pm) {
         if (applicationInfo.isSystemApp()) {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/IntentButtonProvider.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/IntentButtonProvider.java
new file mode 100644
index 0000000..1b8efa7
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/IntentButtonProvider.java
@@ -0,0 +1,41 @@
+/*
+ * 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.systemui.plugins;
+
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+
+/**
+ * An Intent Button represents a triggerable element in SysUI that consists of an
+ * Icon and an intent to trigger when it is activated (clicked, swiped, etc.).
+ */
+public interface IntentButtonProvider extends Plugin {
+
+    public static final int VERSION = 1;
+
+    public IntentButton getIntentButton();
+
+    public interface IntentButton {
+        public static class IconState {
+            public boolean isVisible = true;
+            public CharSequence contentDescription = null;
+            public Drawable drawable;
+        }
+
+        public IconState getIcon();
+
+        public Intent getIntent();
+    }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java
index 495771a..f98667d 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginInstanceManager.java
@@ -14,12 +14,10 @@
 
 package com.android.systemui.plugins;
 
-import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.ContextWrapper;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -33,12 +31,10 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
-import dalvik.system.PathClassLoader;
-
 import java.util.ArrayList;
 import java.util.List;
 
-public class PluginInstanceManager<T extends Plugin> extends BroadcastReceiver {
+public class PluginInstanceManager<T extends Plugin> {
 
     private static final boolean DEBUG = false;
 
@@ -57,20 +53,21 @@
     final PluginHandler mPluginHandler;
     private final boolean isDebuggable;
     private final PackageManager mPm;
-    private final ClassLoaderFactory mClassLoaderFactory;
+    private final PluginManager mManager;
 
     PluginInstanceManager(Context context, String action, PluginListener<T> listener,
-            boolean allowMultiple, Looper looper, int version) {
+            boolean allowMultiple, Looper looper, int version, PluginManager manager) {
         this(context, context.getPackageManager(), action, listener, allowMultiple, looper, version,
-                Build.IS_DEBUGGABLE, new ClassLoaderFactory());
+                manager, Build.IS_DEBUGGABLE);
     }
 
     @VisibleForTesting
     PluginInstanceManager(Context context, PackageManager pm, String action,
             PluginListener<T> listener, boolean allowMultiple, Looper looper, int version,
-            boolean debuggable, ClassLoaderFactory classLoaderFactory) {
+            PluginManager manager, boolean debuggable) {
         mMainHandler = new MainHandler(Looper.getMainLooper());
         mPluginHandler = new PluginHandler(looper);
+        mManager = manager;
         mContext = context;
         mPm = pm;
         mAction = action;
@@ -78,44 +75,29 @@
         mAllowMultiple = allowMultiple;
         mVersion = version;
         isDebuggable = debuggable;
-        mClassLoaderFactory = classLoaderFactory;
     }
 
-    public void startListening() {
+    public void loadAll() {
         if (DEBUG) Log.d(TAG, "startListening");
         mPluginHandler.sendEmptyMessage(PluginHandler.QUERY_ALL);
-        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
-        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        filter.addDataScheme("package");
-        mContext.registerReceiver(this, filter);
-        filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
-        mContext.registerReceiver(this, filter);
     }
 
-    public void stopListening() {
+    public void destroy() {
         if (DEBUG) Log.d(TAG, "stopListening");
         ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins);
         for (PluginInfo plugin : plugins) {
             mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED,
                     plugin.mPlugin).sendToTarget();
         }
-        mContext.unregisterReceiver(this);
     }
 
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        if (DEBUG) Log.d(TAG, "onReceive " + intent);
-        if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
-            mPluginHandler.sendEmptyMessage(PluginHandler.QUERY_ALL);
-        } else {
-            Uri data = intent.getData();
-            String pkgName = data.getEncodedSchemeSpecificPart();
-            mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkgName).sendToTarget();
-            if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
-                mPluginHandler.obtainMessage(PluginHandler.QUERY_PKG, pkgName).sendToTarget();
-            }
-        }
+    public void onPackageRemoved(String pkg) {
+        mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkg).sendToTarget();
+    }
+
+    public void onPackageChange(String pkg) {
+        mPluginHandler.obtainMessage(PluginHandler.REMOVE_PKG, pkg).sendToTarget();
+        mPluginHandler.obtainMessage(PluginHandler.QUERY_PKG, pkg).sendToTarget();
     }
 
     public boolean checkAndDisable(String className) {
@@ -179,12 +161,6 @@
         }
     }
 
-    static class ClassLoaderFactory {
-        public ClassLoader createClassLoader(String path, ClassLoader base) {
-            return new PathClassLoader(path, base);
-        }
-    }
-
     private class PluginHandler extends Handler {
         private static final int QUERY_ALL = 1;
         private static final int QUERY_PKG = 2;
@@ -279,8 +255,7 @@
                     return null;
                 }
                 // Create our own ClassLoader so we can use our own code as the parent.
-                ClassLoader classLoader = mClassLoaderFactory.createClassLoader(info.sourceDir,
-                        getClass().getClassLoader());
+                ClassLoader classLoader = mManager.getClassLoader(info.sourceDir, info.packageName);
                 Context pluginContext = new PluginContextWrapper(
                         mContext.createApplicationContext(info, 0), classLoader);
                 Class<?> pluginClass = Class.forName(cls, true, classLoader);
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java
index 4bf6494..686b4d4 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginManager.java
@@ -14,7 +14,11 @@
 
 package com.android.systemui.plugins;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
 import android.os.Build;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -22,26 +26,31 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 
+import dalvik.system.PathClassLoader;
+
 import java.lang.Thread.UncaughtExceptionHandler;
+import java.util.Map;
 
 /**
  * @see Plugin
  */
-public class PluginManager {
+public class PluginManager extends BroadcastReceiver {
 
     private static PluginManager sInstance;
 
     private final HandlerThread mBackgroundThread;
     private final ArrayMap<PluginListener<?>, PluginInstanceManager> mPluginMap
             = new ArrayMap<>();
+    private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
     private final Context mContext;
     private final PluginInstanceManagerFactory mFactory;
     private final boolean isDebuggable;
     private final PluginPrefs mPluginPrefs;
+    private ClassLoaderFilter mParentClassLoader;
 
     private PluginManager(Context context) {
-        this(context, new PluginInstanceManagerFactory(), Build.IS_DEBUGGABLE,
-                Thread.getDefaultUncaughtExceptionHandler());
+        this(context, new PluginInstanceManagerFactory(),
+                Build.IS_DEBUGGABLE, Thread.getDefaultUncaughtExceptionHandler());
     }
 
     @VisibleForTesting
@@ -72,9 +81,12 @@
         }
         mPluginPrefs.addAction(action);
         PluginInstanceManager p = mFactory.createPluginInstanceManager(mContext, action, listener,
-                allowMultiple, mBackgroundThread.getLooper(), version);
-        p.startListening();
+                allowMultiple, mBackgroundThread.getLooper(), version, this);
+        p.loadAll();
         mPluginMap.put(listener, p);
+        if (mPluginMap.size() == 1) {
+            startListening();
+        }
     }
 
     public void removePluginListener(PluginListener<?> listener) {
@@ -83,7 +95,68 @@
             return;
         }
         if (!mPluginMap.containsKey(listener)) return;
-        mPluginMap.remove(listener).stopListening();
+        mPluginMap.remove(listener).destroy();
+        if (mPluginMap.size() == 0) {
+            stopListening();
+        }
+    }
+
+    private void startListening() {
+        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addDataScheme("package");
+        mContext.registerReceiver(this, filter);
+        filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
+        mContext.registerReceiver(this, filter);
+    }
+
+    private void stopListening() {
+        mContext.unregisterReceiver(this);
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
+            for (PluginInstanceManager manager : mPluginMap.values()) {
+                manager.loadAll();
+            }
+        } else {
+            Uri data = intent.getData();
+            String pkg = data.getEncodedSchemeSpecificPart();
+            clearClassLoader(pkg);
+            if (!Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+                for (PluginInstanceManager manager : mPluginMap.values()) {
+                    manager.onPackageChange(pkg);
+                }
+            } else {
+                for (PluginInstanceManager manager : mPluginMap.values()) {
+                    manager.onPackageRemoved(pkg);
+                }
+            }
+        }
+    }
+
+    public ClassLoader getClassLoader(String sourceDir, String pkg) {
+        if (mClassLoaders.containsKey(pkg)) {
+            return mClassLoaders.get(pkg);
+        }
+        ClassLoader classLoader = new PathClassLoader(sourceDir, getParentClassLoader());
+        mClassLoaders.put(pkg, classLoader);
+        return classLoader;
+    }
+
+    private void clearClassLoader(String pkg) {
+        mClassLoaders.remove(pkg);
+    }
+
+    ClassLoader getParentClassLoader() {
+        if (mParentClassLoader == null) {
+            // Lazily load this so it doesn't have any effect on devices without plugins.
+            mParentClassLoader = new ClassLoaderFilter(getClass().getClassLoader(),
+                    "com.android.systemui.plugin");
+        }
+        return mParentClassLoader;
     }
 
     public static PluginManager getInstance(Context context) {
@@ -97,9 +170,29 @@
     public static class PluginInstanceManagerFactory {
         public <T extends Plugin> PluginInstanceManager createPluginInstanceManager(Context context,
                 String action, PluginListener<T> listener, boolean allowMultiple, Looper looper,
-                int version) {
+                int version, PluginManager manager) {
             return new PluginInstanceManager(context, action, listener, allowMultiple, looper,
-                    version);
+                    version, manager);
+        }
+    }
+
+
+    // This allows plugins to include any libraries or copied code they want by only including
+    // classes from the plugin library.
+    private static class ClassLoaderFilter extends ClassLoader {
+        private final String mPackage;
+        private final ClassLoader mBase;
+
+        public ClassLoaderFilter(ClassLoader base, String pkg) {
+            super(ClassLoader.getSystemClassLoader());
+            mBase = base;
+            mPackage = pkg;
+        }
+
+        @Override
+        protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+            if (!name.startsWith(mPackage)) super.loadClass(name, resolve);
+            return mBase.loadClass(name);
         }
     }
 
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
new file mode 100644
index 0000000..09879d8
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavBarButtonProvider.java
@@ -0,0 +1,51 @@
+/*
+ * 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.systemui.plugins.statusbar.phone;
+
+import android.annotation.DrawableRes;
+import android.annotation.Nullable;
+import android.graphics.drawable.Drawable;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.systemui.plugins.Plugin;
+
+public interface NavBarButtonProvider extends Plugin {
+
+    public static final String ACTION = "com.android.systemui.action.PLUGIN_NAV_BUTTON";
+
+    public static final int VERSION = 1;
+
+    /**
+     * Returns a view in the nav bar.  If the id is set "back", "home", "recent_apps", "menu",
+     * or "ime_switcher", it is expected to implement ButtonInterface.
+     */
+    public View createView(String spec, ViewGroup parent);
+
+    /**
+     * Interface for button actions.
+     */
+    interface ButtonInterface {
+        void setImageResource(@DrawableRes int resId);
+
+        void setImageDrawable(@Nullable Drawable drawable);
+
+        void abortCurrentGesture();
+
+        void setLandscape(boolean landscape);
+
+        void setCarMode(boolean carMode);
+    }
+}
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 9fc0b6e..7d3eb8d 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G-data is laat wag"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Sellulêre data is onderbreek"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Data is onderbreek"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Jy het die datalimiet wat jy gestel het, bereik. Jy gebruik nie meer sellulêre data nie.\n\nAs jy voortgaan, kan heffings vir datagebruik geld."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Hervat"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Geen internetverbinding nie"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi gekoppel"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 0e49ab96..fb73729 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4ጂ ውሂብ ላፍታ ቆሟል"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"የተንቀሳቃሽ ስልክ ውሂብ ላፍታ ቆሟል"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"ውሂብ ላፍታ ቆሟል"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"እርስዎ ያስቀመጡት የውሂብ ገደብ ላይ ተደርሷል። ከእንግዲህ ተንቀሳቃሽ ውሂብ እየተጠቀሙ አይደለም ያሉት።\n\nከቆመበት ከቀጠሉ የውሂብ አጠቃቀም ክፍያዎች ተፈጻሚ ሊሆኑ ይችላሉ።"</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"ከቆመበት ቀጥል"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"ምንም በይነመረብ ተያያዥ የለም።"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi ተያይዟል"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index abd32b7..b7bb97e 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -241,8 +241,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"تم إيقاف بيانات شبكة الجيل الرابع مؤقتًا"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"تم إيقاف بيانات شبكة الجوّال مؤقتًا"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"تم إيقاف البيانات مؤقتًا"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"تم الوصول إلى حد البيانات الذي عيَّنته، ولم يعد بإمكانك استخدام بيانات شبكة الجوّال.\n\nعند الاستئناف، قد يتم تحصيل رسوم مقابل استخدام البيانات."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"استئناف"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"لا يوجد اتصال إنترنت"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"‏Wi-Fi متصل"</string>
diff --git a/packages/SystemUI/res/values-az-rAZ/strings.xml b/packages/SystemUI/res/values-az-rAZ/strings.xml
index ca54794..ee98b8f 100644
--- a/packages/SystemUI/res/values-az-rAZ/strings.xml
+++ b/packages/SystemUI/res/values-az-rAZ/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G məlumatlarına fasilə verildi"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobil məlumatlara fasilə verildi"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Məlumatlara fasilə verildi"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Daha limitini keçdiniz. Artıq mobil data istifadə etmirsiniz.\n\nDavam etsəniz, data istifadəsi üçün ödəniş tətbiq oluna bilər."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Davam et"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"İnternet bağlantısı yoxdur"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi qoşulub"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index d22928d..ac8699b 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -238,8 +238,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G podaci su pauzirani"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobilni podaci su pauzirani"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Podaci su pauzirani"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Ograničenje potrošnje podataka koje ste podesili je dostignuto. Više ne koristite mobilne podatke.\n\nAko nastavite, možda će biti naplaćeni troškovi za potrošnju podataka."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Nastavi"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Nema internet veze"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi je povezan"</string>
diff --git a/packages/SystemUI/res/values-be-rBY/strings.xml b/packages/SystemUI/res/values-be-rBY/strings.xml
index c215680..fb43971 100644
--- a/packages/SystemUI/res/values-be-rBY/strings.xml
+++ b/packages/SystemUI/res/values-be-rBY/strings.xml
@@ -241,8 +241,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Перадача даных 4G прыпынена"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Мабільная перадача даных прыпынена"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Перадача даных прыпынена"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Ліміт даных, які вы задалі, быў дасягнуты. Вы больш не выкарыстоўваеце сотавую перадачу даных.\n\nКалі вы ўзновіце карыстанне, можа спаганяцца плата за выкарыстанне трафіка."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Узнавіць"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Няма падключэння да Iнтэрнэту"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi падключаны"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index c2a2a92..8ef1c85 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Данните от 4G са поставени на пауза"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Мобилните данни са поставени на пауза"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Данните са поставени на пауза"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Достигнахте зададеното от вас ограничение за данните. Вече не използвате мобилната мрежа.\n\nАко възобновите връзката с нея, може да бъдете таксувани за пренос на данни."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Възобновяване"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Няма връзка с интернет"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi: Има връзка"</string>
diff --git a/packages/SystemUI/res/values-bn-rBD/strings.xml b/packages/SystemUI/res/values-bn-rBD/strings.xml
index 5ed264e..3ef8892 100644
--- a/packages/SystemUI/res/values-bn-rBD/strings.xml
+++ b/packages/SystemUI/res/values-bn-rBD/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G ডেটা বিরতি দেওয়া হয়েছে"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"সেলুলার ডেটা বিরতি দেওয়া হয়েছে"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"ডেট বিরতি দেওয়া হয়েছে"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"আপনার সেটা করা ডেটা সীমা ছাড়িয়ে গেছে৷ আপনি আর সেলুলার ডেটা ব্যবহার করতে পারবেন না৷\n\nআপনি যদি আবার ব্যবহার করতে শুরু করেন তাহলে ডেটা ব্যবহারের জন্য চার্জ লাগতে পারে৷"</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"পুনঃসূচনা করুন"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"কোনো ইন্টারনেট সংযোগ নেই"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"ওয়াই-ফাই সংযুক্ত হয়েছে"</string>
diff --git a/packages/SystemUI/res/values-bs-rBA/strings.xml b/packages/SystemUI/res/values-bs-rBA/strings.xml
index c621ecd..6ce6dbe 100644
--- a/packages/SystemUI/res/values-bs-rBA/strings.xml
+++ b/packages/SystemUI/res/values-bs-rBA/strings.xml
@@ -238,8 +238,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G prijenos podataka je pauzirano"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobilni podaci su pauzirani"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Prijenos podataka je pauziran"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Dostigli ste ograničenje za prijenos podataka koje ste postavili. Više ne koristite mobilne podatke.\n\nUkoliko nastavite koristiti mobilne podatke, mogući su troškovi za prijenos podataka."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Nastavi"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Nema internet veze"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi veza aktivna"</string>
@@ -314,7 +313,7 @@
     <string name="quick_settings_notifications_label" msgid="4818156442169154523">"Obavještenja"</string>
     <string name="quick_settings_flashlight_label" msgid="2133093497691661546">"Svjetiljka"</string>
     <string name="quick_settings_cellular_detail_title" msgid="8575062783675171695">"Mobilni podaci"</string>
-    <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Korištenje podataka"</string>
+    <string name="quick_settings_cellular_detail_data_usage" msgid="1964260360259312002">"Prijenos podataka"</string>
     <string name="quick_settings_cellular_detail_remaining_data" msgid="722715415543541249">"Preostala količina podataka"</string>
     <string name="quick_settings_cellular_detail_over_limit" msgid="967669665390990427">"Prekoračeno"</string>
     <string name="quick_settings_cellular_detail_data_used" msgid="1476810587475761478">"Iskorišteno <xliff:g id="DATA_USED">%s</xliff:g>"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 882c8e3..8810ce3 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Les dades 4G estan aturades"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Les dades mòbils estan aturades"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Les dades estan aturades"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"S\'ha assolit el límit de dades establert. Ja no estàs utilitzant dades mòbils. \n\n Si reprens l\'ús de les dades, es poden aplicar càrrecs."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Reprèn"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"No hi ha connexió a Internet"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi: connectada"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 216e763..4a515a6 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -241,8 +241,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Data 4G jsou pozastavena"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobilní data jsou pozastavena"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Data jsou pozastavena"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Bylo dosaženo limitu dat. Používání mobilních dat bylo vypnuto.\n\nPokud jej obnovíte, mohou vám být účtovány poplatky za využití dat."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Pokračovat"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Žádné přip. k internetu"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi: připojeno"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 77727bb..d23bd26 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G-data er sat på pause"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobildata er sat på pause"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Data er sat på pause"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Du har nået den angivne datagrænse. Du vil ikke længere bruge mobildata.\n\nHvis du fortsætter, vil du muligvis blive opkrævet betaling for dit dataforbrug."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Genoptag"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Ingen internetforb."</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi er forbundet"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 5aa99a1..3ba8f5dd 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -239,8 +239,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G-Daten pausiert"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobilfunkdaten pausiert"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Daten pausiert"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Das von dir festgelegte Datenlimit wurde erreicht. Die mobile Datennutzung wurde deaktiviert.\n\nWenn du weiterhin mobile Daten nutzt, können Gebühren anfallen."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Fortsetzen"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Keine Internetverbindung"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"WLAN verbunden"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 4c5718b..fc248cf 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Τα δεδομένα 4G τέθηκαν σε παύση"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Τα δεδομένα κινητής τηλεφωνίας τέθηκαν σε παύση"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Τα δεδομένα τέθηκαν σε παύση"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Το όριο δεδομένων που ορίσατε έχει εξαντληθεί. Δεν χρησιμοποιείτε πλέον δεδομένα κινητής τηλεφωνίας.\n\nΑν συνεχίσετε, ενδέχεται να ισχύσουν χρεώσεις για τη χρήση δεδομένων."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Συνέχιση"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Χωρ. σύνδ. στο Διαδ."</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi συνδεδεμένο"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index d12b448..75ec92f 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G data is paused"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobile data is paused"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Data is paused"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"The data limit you have set has been reached. You are no longer using mobile data.\n\nIf you resume, charges may apply for data usage."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Resume"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"No Internet connection"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi connected"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index d12b448..75ec92f 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G data is paused"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobile data is paused"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Data is paused"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"The data limit you have set has been reached. You are no longer using mobile data.\n\nIf you resume, charges may apply for data usage."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Resume"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"No Internet connection"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi connected"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index d12b448..75ec92f 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G data is paused"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobile data is paused"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Data is paused"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"The data limit you have set has been reached. You are no longer using mobile data.\n\nIf you resume, charges may apply for data usage."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Resume"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"No Internet connection"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi connected"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 45198cf..5c98ae7 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -239,8 +239,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Datos 4G pausados"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Datos móviles pausados"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Datos pausados"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Se alcanzó el límite de datos que estableciste. Ya no estás usando datos móviles.\n\nSi reanudas el uso de datos, es posible que se apliquen cargos."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Reanudar"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Sin conexión a Internet"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi conectado"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 3d69e5e..4da5ba4 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -239,8 +239,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Datos 4G pausados"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Datos móviles pausados"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Datos pausados"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Se ha alcanzado el límite de datos establecido. Ya no estás utilizando datos móviles.\n\nSi vuelves a activar el uso de datos, es posible que se apliquen cargos."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Reanudar"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Sin conexión a Internet"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Con conexión Wi-Fi"</string>
diff --git a/packages/SystemUI/res/values-et-rEE/strings.xml b/packages/SystemUI/res/values-et-rEE/strings.xml
index 17cbc13..0a09e48 100644
--- a/packages/SystemUI/res/values-et-rEE/strings.xml
+++ b/packages/SystemUI/res/values-et-rEE/strings.xml
@@ -239,8 +239,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G andmekasutus on peatatud"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobiilse andmeside kasutus on peatatud"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Andmekasutus on peatatud"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Olete jõudnud enda määratud andmemahupiiranguni. Te ei kasuta enam mobiilset andmesidet.\n\nKui jätkate, võivad rakenduda andmekasutustasud."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Jätka"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Interneti-ühendus puudub"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"WiFi on ühendatud"</string>
diff --git a/packages/SystemUI/res/values-eu-rES/strings.xml b/packages/SystemUI/res/values-eu-rES/strings.xml
index 5715c506..74b3a61 100644
--- a/packages/SystemUI/res/values-eu-rES/strings.xml
+++ b/packages/SystemUI/res/values-eu-rES/strings.xml
@@ -239,8 +239,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G datuen erabilera eten da"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Sare mugikorreko datuen erabilera eten da"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Datuen erabilera eten da"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Gainditu egin da ezarri duzun datu-muga. Datu-konexioa erabiltzeari utzi diozu.\n\nBerriro hasten bazara erabiltzen, baliteke datuen erabileraren kostua ordaindu behar izatea."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Jarraitu erabiltzen"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Ez duzu Interneteko konexiorik"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi konektatuta"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index d92b4c0..1ccc804 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"‏داده 4G موقتاً متوقف شده است"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"داده شبکه همراه موقتاً متوقف شده است"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"داده موقتاً متوقف شده است"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"به حداکثر محدودیت داده‌ای که تنظیم کردید رسیدید. دیگر از داده شبکه تلفن همراه استفاده نمی‌کنید.\n\nدر صورت ازسرگیری، ممکن است مصرف داده هزینه‌هایی دربرداشته باشد."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"از سر‌گیری"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"اتصال اینترنتی ندارید"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"‏Wi-Fi متصل شد"</string>
@@ -484,7 +483,7 @@
     <string name="accessibility_managed_profile" msgid="6613641363112584120">"نمایه کاری"</string>
     <string name="tuner_warning_title" msgid="7094689930793031682">"برای بعضی افراد سرگرم‌کننده است اما نه برای همه"</string>
     <string name="tuner_warning" msgid="8730648121973575701">"‏«تنظیم‌کننده واسط کاربری سیستم» روش‌های بیشتری برای تنظیم دقیق و سفارشی کردن واسط کاربری Android در اختیار شما قرار می‌دهد. ممکن است این ویژگی‌های آزمایشی تغییر کنند، خراب شوند یا در نسخه‌های آینده جود نداشته باشند. با احتیاط ادامه دهید."</string>
-    <string name="tuner_persistent_warning" msgid="8597333795565621795">"ممکن است این ویژگی‌های آزمایشی تغییر کنند، خراب شوند یا در نسخه‌های آینده وجود نداشته باشند. با احتیاط ادامه دهید."</string>
+    <string name="tuner_persistent_warning" msgid="8597333795565621795">"ممکن است این قابلیت‌های آزمایشی تغییر کنند، خراب شوند یا در نسخه‌های آینده وجود نداشته باشد. بااحتیاط ادامه دهید."</string>
     <string name="got_it" msgid="2239653834387972602">"متوجه شدم"</string>
     <string name="tuner_toast" msgid="603429811084428439">"تبریک می‌گوییم! «تنظیم‌کننده واسط کاربری سیستم» به «تنظیمات» اضافه شد"</string>
     <string name="remove_from_settings" msgid="8389591916603406378">"حذف از تنظیمات"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 7bb7dd6..9ec1ffd 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G-tiedonsiirto keskeytettiin"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobiilitiedonsiirto keskeytettiin"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Tiedonsiirto keskeytettiin"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Määrittämäsi datankäyttöraja on täynnä. Mobiilidata poistettiin käytöstä.\n\nOperaattorisi voi veloittaa sinua, jos jatkat mobiilidatan käyttöä."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Jatka"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Ei internetyhteyttä"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi yhdistetty"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index b0215bd..67e717e 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -239,8 +239,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Données 4G désactivées"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Données cellulaires désactivées"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Données désactivées"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"La limite de données que vous avez définie a été atteinte. Vous n\'utilisez plus les données cellulaires.\n\nSi vous rétablissez la connexion de données cellulaires, des frais peuvent s\'appliquer."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Reprendre"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Aucune connexion Internet"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Connecté au Wi-Fi"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 2a37637..7310a47 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -239,8 +239,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Données 4G désactivées"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Données mobiles désactivées"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Données désactivées"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"La limite de consommation des données que vous avez définie a été atteinte. Vous n\'utilisez plus les données mobiles.\n\nSi vous les réactivez, des frais pourront être facturés."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Réactiver"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Aucune connexion Internet"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Connecté au Wi-Fi"</string>
@@ -448,7 +447,7 @@
   <string-array name="volume_stream_titles">
     <item msgid="5841843895402729630">"Appeler"</item>
     <item msgid="5997713001067658559">"Système"</item>
-    <item msgid="7858983209929864160">"Faire sonner"</item>
+    <item msgid="7858983209929864160">"Sonnerie"</item>
     <item msgid="1850038478268896762">"Multimédia"</item>
     <item msgid="8265110906352372092">"Alarme"</item>
     <item msgid="5339394737636839168"></item>
diff --git a/packages/SystemUI/res/values-gl-rES/strings.xml b/packages/SystemUI/res/values-gl-rES/strings.xml
index 40c5ca7..6152872 100644
--- a/packages/SystemUI/res/values-gl-rES/strings.xml
+++ b/packages/SystemUI/res/values-gl-rES/strings.xml
@@ -239,8 +239,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Os datos 4G están en pausa"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Os datos de móbiles están en pausa"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Os datos están en pausa"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Alcanzouse o límite de datos establecido e xa non utilizas datos móbiles.\n\nSe continúas, é posible que se apliquen cargos por uso de datos."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Retomar"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Sen Internet"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi conectada"</string>
diff --git a/packages/SystemUI/res/values-gu-rIN/strings.xml b/packages/SystemUI/res/values-gu-rIN/strings.xml
index 5b3c9c2..26b1c06 100644
--- a/packages/SystemUI/res/values-gu-rIN/strings.xml
+++ b/packages/SystemUI/res/values-gu-rIN/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G ડેટા થોભાવ્યો છે"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"સેલ્યુલર ડેટા થોભાવ્યો છે"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"ડેટા થોભાવ્યો છે"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"તમારા દ્વારા સેટ કરેલ ડેટા મર્યાદા પર તમે પહોંચી ગયાં છો. તમે હવે સેલ્યુલર ડેટાનો ઉપયોગ કરી રહ્યાં નથી.\n\nજો તમે ફરી શરૂ કરો છો, તો ડેટા વપરાશ માટે શુલ્ક લાગુ થઈ શકે છે."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"ફરી શરૂ કરો"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"કોઈ ઇન્ટરનેટ કનેક્શન નથી"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi કનેક્ટ કર્યું"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 217a5f1..42c049d 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G डेटा रोक दिया गया है"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"सेल्युलर डेटा रोक दिया गया है"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"डेटा रोक दिया गया है"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"आपकी सेट की हुई डेटा सीमा समाप्त हो गई है. अब आप सेल्युलर डेटा का उपयोग नहीं कर रहे हैं.\n\nयदि आप फिर से शुरू करते हैं, तो डेटा उपयोग के लिए शुल्क लग सकता है."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"फिर से शुरू करें"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"कोई इंटरनेट कनेक्शन नहीं"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"वाई-फ़ाई  कनेक्‍ट किया गया"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index a9815d5..58dd92e 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -238,8 +238,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G podaci pauzirani"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobilni podaci pauzirani"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Podaci su pauzirani"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Dosegnuto je vaše ograničenje podataka. Više ne upotrebljavate mobilne podatke.\n\nAko nastavite, moguća je naplata za potrošnju podataka."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Nastavi"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Nema internetske veze"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi povezan"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index 9fd4eb7..0804093 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"A 4G adatforgalom szünetel"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"A mobilhálózati adatforgalom szünetel"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Az adatforgalom szünetel"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Elérte a beállított adatkorlátot. A továbbiakban nem használ mobiladat-forgalmat.\n\nHa a folytatást választja, szolgáltatója adathasználati díjat számíthat fel."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Folytatás"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Nincs internet"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi csatlakoztatva"</string>
diff --git a/packages/SystemUI/res/values-hy-rAM/strings.xml b/packages/SystemUI/res/values-hy-rAM/strings.xml
index 236663d..1b0f3d7 100644
--- a/packages/SystemUI/res/values-hy-rAM/strings.xml
+++ b/packages/SystemUI/res/values-hy-rAM/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4Գ տվյալների օգտագործումը դադարեցված է"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Բջջային տվյալների օգտագործումը դադարեցված է"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Տվյալների օգտագործումը դադարեցված է"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Տվյալների օգտագործման համար նշված սահմանաչափը լրացել է: Դուք բջջային տվյալներ այլևս չեք օգտագործում:\n\nԵթե վերսկսեք բջջային տվյալների օգտագործումը, դրա համար կարող են վճարներ գանձվել:"</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Վերսկսել"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Ինտերնետ կապ չկա"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi-ը միացված է"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 5e26008..be27eca 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Data 4G dijeda"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Data seluler dijeda"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Data dijeda"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Batas data yang disetel telah tercapai. Anda tidak menggunakan data seluler lagi.\n\nJika Anda melanjutkan, biaya penggunaan data mungkin berlaku."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Lanjutkan"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Tidak ada sambungan internet"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi tersambung"</string>
diff --git a/packages/SystemUI/res/values-is-rIS/strings.xml b/packages/SystemUI/res/values-is-rIS/strings.xml
index 02b59b6..0b65a95 100644
--- a/packages/SystemUI/res/values-is-rIS/strings.xml
+++ b/packages/SystemUI/res/values-is-rIS/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Slökkt er á 4G-gögnum"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Slökkt er á farsímagögnum"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Slökkt er á gagnanotkun"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Gagnamörkunum sem þú stilltir hefur verið náð. Þú ert ekki lengur að nota farsímagögn.\n\nEf þú heldur áfram gætu gjöld fyrir gagnanotkun átt við."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Halda áfram"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Engin nettenging"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi tengt"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 9d76477..5eae72c 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -239,8 +239,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Dati 4G sospesi"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Dati cellulari sospesi"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Dati sospesi"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"È stato raggiunto il limite di dati impostato. La rete dati è stata disattivata.\n\nSe la riattivi, potrebbero essere applicati costi per l\'utilizzo dei dati."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Riprendi"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Nessuna connessione"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi connesso"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index 3429497..ef4d683 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -239,8 +239,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"‏השימוש בנתוני 4G מושהה"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"השימוש בנתונים סלולריים מושהה"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"השימוש בנתונים מושהה"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"הגעת למגבלת הנתונים שהגדרת. אתה כבר לא משתמש בנתונים סלולריים.\n\nאם תמשיך, ייתכנו חיובים לשימוש בנתונים."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"המשך"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"אין חיבור לאינטרנט"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"‏Wi-Fi מחובר"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index bbd8033..fcc5ae1 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -239,8 +239,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4Gデータは一時停止中です"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"モバイルデータは一時停止中です"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"データの一時停止"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"設定されたデータの上限に達しているため、モバイルデータの使用を停止しました。\n\n再開すると、携帯通信会社からデータ使用量に応じた通信料を課金される可能性があります。"</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"再開"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"インターネット未接続"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi接続済み"</string>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index c00c54c..b799116 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G მონაცემები შეჩერებულია"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"ფიჭური მონაცემები შეჩერებულია"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"მონაცემები შეჩერებულია"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"მიღწეულია მონაცემთა მოხმარების თქვენ მიერ მითითებული ლიმიტი. ამიტომ, მობილური ინტერნეტის გამოყენება აღარ ხდება.\n\nგანახლების შემთხვევაში, შეიძლება მობილური ინტერნეტის საფასურის გადახდა მოგიწიოთ."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"გაგრძელება"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"ინტერნეტ კავშირი არ არის"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi დაკავშირებულია"</string>
diff --git a/packages/SystemUI/res/values-kk-rKZ/strings.xml b/packages/SystemUI/res/values-kk-rKZ/strings.xml
index 8051cd8..872caa7 100644
--- a/packages/SystemUI/res/values-kk-rKZ/strings.xml
+++ b/packages/SystemUI/res/values-kk-rKZ/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G деректері кідіртілді"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Ұялы деректер кідіртілді"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Деректер кідіртілді"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Белгіленген деректер шегіне жеттіңіз. Ұялы деректер енді пайдаланылмайды.\n\nЕгер жалғастырсаңыз, деректер трафигі үшін ақы алынуы мүмкін."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Жалғастыру"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Интернет байланысы жоқ"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi қосулы"</string>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index c0d002c..ee63681 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"ទិន្នន័យ 4G ត្រូវបានផ្អាក"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"ទិន្នន័យចល័តត្រូវបានផ្អាក"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"ទិន្នន័យត្រូវបានផ្អាក"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"បានឈានដល់កម្រិតទិន្នន័យដែលអ្នកបានកំណត់ហើយ។ អ្នកមិនអាចប្រើទិន្នន័យចល័តបានទៀតទេ។\n\nអាចនឹងគិតថ្លៃលើការប្រើទិន្នន័យ ប្រសិនបើអ្នកបន្តប្រើ។"</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"បន្ត"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"គ្មាន​ការ​តភ្ជាប់​អ៊ីនធឺណិត"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"បាន​ភ្ជាប់​វ៉ាយហ្វាយ"</string>
diff --git a/packages/SystemUI/res/values-kn-rIN/strings.xml b/packages/SystemUI/res/values-kn-rIN/strings.xml
index e1c42d7..189cce0 100644
--- a/packages/SystemUI/res/values-kn-rIN/strings.xml
+++ b/packages/SystemUI/res/values-kn-rIN/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G ಡೇಟಾ ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"ಸೆಲ್ಯುಲಾರ್ ಡೇಟಾ ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"ಡೇಟಾ ವಿರಾಮಗೊಳಿಸಲಾಗಿದೆ"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"ನೀವು ಹೊಂದಿಸಿರುವ ಡೇಟಾ ಮಿತಿ ತಲುಪಿದೆ. ನೀವು ಎಂದಿಗೂ ಸೆಲ್ಯುಲಾರ್ ಡೇಟಾವನ್ನು ಬಳಸದೆ ಇರಬಹುದು.\n\nನೀವು ಮುಂದುವರಿಸಿದರೆ, ಡೇಟಾ ಬಳಕೆಗೆ ಶುಲ್ಕಗಳು ಅನ್ವಯವಾಗಬಹುದು."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"ಮುಂದುವರಿಸು"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"ಇಂಟರ್ನೆಟ್ ಸಂಪರ್ಕವಿಲ್ಲ"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"ವೈ-ಫೈ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index 1c97212..af40128 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -239,8 +239,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G 데이터 사용 중지됨"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"모바일 데이터 사용 중지됨"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"데이터 사용 중지됨"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"설정한 데이터 한도에 도달했습니다. 더 이상 모바일 데이터를 사용하지 않습니다.\n\n계속 사용하면 데이터 사용 요금이 부과될 수 있습니다."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"재개"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"인터넷에 연결되지 않음"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi 연결됨"</string>
diff --git a/packages/SystemUI/res/values-ky-rKG/strings.xml b/packages/SystemUI/res/values-ky-rKG/strings.xml
index b17f523..62775d1 100644
--- a/packages/SystemUI/res/values-ky-rKG/strings.xml
+++ b/packages/SystemUI/res/values-ky-rKG/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G дайындары тындырылды"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Уюлдук дайындар тындырылды"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Дайындар тындырылды"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Сиз койгон дайындардын чегине жетти. Сиз мобилдик дайындарды колдонгон жоксуз.\n\nЭгер улантсаңыз, мобилдик дайындарды колдонгонуңуз үчүн акы алынышы мүмкүн."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Улантуу"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Интернет байланыш жок"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi байланышта"</string>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index 0dc3d50..81ee168 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"ຂໍ້​ມູນ 4G ຢຸດ​ຊົ່ວ​ຄາວແລ້ວ"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"ຂໍ້​ມູນເຊວ​ລູ​ລາຢຸດ​ຊົ່ວ​ຄາວແລ້ວ"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"ຂໍ້​ມູນ​ຢຸດ​ຊົ່ວ​ຄາວແລ້ວ"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"ທ່ານໃຊ້ອິນເຕີເນັດຮອດຈຳນວນທີ່ທ່ານຈຳກັດປະລິມານໄວ້ແລ້ວ. ທ່ານຈະບໍ່ນຳໃຊ້ການເຊື່ອມຕໍ່ຜ່ານເຄືອຂ່າຍມືຖືອີກຕໍ່ໄປ.\n\nຫາກທ່ານສືບຕໍ່ໃຊ້ໄປອີກ, ທ່ານອາດຖືກຮຽກເກັບເງິນຄ່າບໍລິການໄດ້."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"ເລີ່ມຕໍ່"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"ເຊື່ອມ​ຕໍ່ Wi-​-Fi ແລ້ວ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index dd2f198..df1765a 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -239,8 +239,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G duomenys pristabdyti"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Korinio ryšio duomenys pristabdyti"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Duomenys pristabdyti"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Pasiektas nustatytas duomenų apribojimas. Nebenaudojate mobiliojo ryšio duomenų.\n\nJei atnaujinsite, gali būti taikomi mokesčiai už duomenų naudojimą."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Atnaujinti"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Nėra interneto ryš."</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Prisij. prie „Wi-Fi“"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 4242497..ffad77b 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -238,8 +238,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G datu lietojums ir apturēts"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobilo datu lietojums ir apturēts"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Datu lietojums ir apturēts"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Ir sasniegts jūsu iestatītais datu ierobežojums. Jūs vairs neizmantojat mobilos datus.\n\nJa atsāksiet, var tikt piemērota maksa par datu lietojumu."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Atsākt"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Nav interneta sav."</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Izv. sav. ar Wi-Fi"</string>
diff --git a/packages/SystemUI/res/values-mk-rMK/strings.xml b/packages/SystemUI/res/values-mk-rMK/strings.xml
index 4091bbe..4aa1821 100644
--- a/packages/SystemUI/res/values-mk-rMK/strings.xml
+++ b/packages/SystemUI/res/values-mk-rMK/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Податоците 4G се паузирани"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Мобилните податоци се паузирани"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Податоците се паузирани"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Го достигнавте ограничувањето за сообраќај на податоци што сте го поставиле. Веќе не користите мобилен интернет.\n\nДоколку продолжите, ќе ви биде наплатено за потрошениот сообраќај."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Продолжи"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Нема интернет"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Поврзано на Wi-Fi"</string>
diff --git a/packages/SystemUI/res/values-ml-rIN/strings.xml b/packages/SystemUI/res/values-ml-rIN/strings.xml
index 6ae547e..225a284 100644
--- a/packages/SystemUI/res/values-ml-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ml-rIN/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G ഡാറ്റ താൽക്കാലികമായി നിർത്തി"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"സെല്ലുലാർ ഡാറ്റ താൽക്കാലികമായി നിർത്തി"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"ഡാറ്റ താൽക്കാലികമായി നിർത്തി"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"നിങ്ങൾ സജ്ജമാക്കിയ ഡാറ്റ പരിധി എത്തിക്കഴിഞ്ഞു. ഇനിയങ്ങോട്ട് നിങ്ങൾ സെല്ലുലാർ ഡാറ്റ ഉപയോഗിക്കില്ല.\n\nതുടരുകയാണെങ്കിൽ, ഡാറ്റാ ഉപയോഗത്തിന് നിരക്കുകൾ ബാധകമായേക്കാം."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"പുനരാരംഭിക്കുക"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"ഇന്റർനെറ്റ് കണക്ഷൻ ഇല്ല"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"വൈഫൈ കണക്‌റ്റുചെയ്‌തു"</string>
diff --git a/packages/SystemUI/res/values-mn-rMN/strings.xml b/packages/SystemUI/res/values-mn-rMN/strings.xml
index 95cbcae..a84aae6 100644
--- a/packages/SystemUI/res/values-mn-rMN/strings.xml
+++ b/packages/SystemUI/res/values-mn-rMN/strings.xml
@@ -235,8 +235,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G дата-г түр зогсоосон байна"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Гар утасны дата-г түр зогсоосон байна"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Дата-г түр зогсоосон байна"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Таны тогтоосон дата хэмжээний хязгаарт хүрсэн байна. Та одоогоор мобайл датаг ашиглаагүй байна.\n\nҮргэлжлүүлсэн тохиолдолд төлбөр гарах болно."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Үргэлжлүүлэх"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Интернет холболт байхгүй"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi холбогдсон"</string>
diff --git a/packages/SystemUI/res/values-mr-rIN/strings.xml b/packages/SystemUI/res/values-mr-rIN/strings.xml
index 830cda4..6c92213 100644
--- a/packages/SystemUI/res/values-mr-rIN/strings.xml
+++ b/packages/SystemUI/res/values-mr-rIN/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G डेटास विराम दिला आहे"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"सेल्युलर डेटास विराम दिला आहे"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"डेटास विराम दिला आहे"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"आपण सेट केलेली डेटा मर्यादा गाठली आहे. आपण यापुढे मोबाइल डेटा वापरणार नाही.\n\nआपण पुन: सुरु केल्यास, डेटा वापरासाठी शुल्क आकारले जाऊ शकतात."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"पुन्हा सुरु करा"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"इंटरनेट कनेक्शन नाही"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"वाय-फाय कनेक्ट केले"</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 82de7a3..95e7a4d 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Data 4G dijeda"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Data selular dijeda"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Data dijeda"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Had data yang anda tetapkan telah dicapai. Anda tidak lagi menggunakan data selular.\n\nJika anda menyambung semula, caj mungkin dikenakan untuk penggunaan data."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Sambung semula"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Tiada smbg Internet"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi disambungkan"</string>
diff --git a/packages/SystemUI/res/values-my-rMM/strings.xml b/packages/SystemUI/res/values-my-rMM/strings.xml
index 3899b00..905c865 100644
--- a/packages/SystemUI/res/values-my-rMM/strings.xml
+++ b/packages/SystemUI/res/values-my-rMM/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G data ခေတ္တရပ်တန့်သည်"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"cellular data ခေတ္တရပ်တန့်သည်"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"ဒေတာ ခေတ္တရပ်တန့်သည်"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"သင်သတ်မှတ်ထားသော ဒေတာကန့်သတ်ချက်သို့ ရောက်နေပါပြီ။ သင်သည် ဆယ်လူလာဒေတာကို အသုံးမပြုတော့ပါ။\n\nသင်ဆက်လုပ်မည်ဆိုလျှင် ဒေတာသုံးစွဲမှုအတွက် အခငွေ ကျသင့်မှုရှိနိုင်ပါသည်။"</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"ပြန်ဆက်လုပ်ရန်"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"အင်တာနက်မရှိ"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"ကြိုးမဲ့ဆက်သွယ်မှု"</string>
@@ -588,7 +587,7 @@
     <string name="center" msgid="4327473927066010960">"ဌာန"</string>
     <string name="end" msgid="125797972524818282">"ပြီးပါပြီ"</string>
     <string name="space" msgid="804232271282109749">"နေရာလွတ်ခြားစနစ်"</string>
-    <string name="menu_ime" msgid="4943221416525250684">"မန်နယူး / ကီးဘုတ်ပြောင်းစနစ်"</string>
+    <string name="menu_ime" msgid="4943221416525250684">"မီနူး / ကီးဘုတ်ပြောင်းစနစ်"</string>
     <string name="select_button" msgid="1597989540662710653">"ပေါင်းထည့်ရန် ခလုတ်ကိုရွေးပါ"</string>
     <string name="add_button" msgid="4134946063432258161">"ခလုတ်ပေါင်းထည့်ပါ"</string>
     <string name="save" msgid="2311877285724540644">"သိမ်းရန်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 0a4e045..41d70e6 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G-data er satt på pause"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobildata er satt på pause"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Data er satt på pause"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Datagrensen du satte, er nådd. Du bruker ikke mobildata lenger.\n\nHvis du gjenopptar bruk av mobildata, kan gebyrer for databruk påløpe."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Gjenoppta"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Ingen Internett-forbindelse"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi tilkoblet"</string>
diff --git a/packages/SystemUI/res/values-ne-rNP/strings.xml b/packages/SystemUI/res/values-ne-rNP/strings.xml
index 47d326c..d0dfd66 100644
--- a/packages/SystemUI/res/values-ne-rNP/strings.xml
+++ b/packages/SystemUI/res/values-ne-rNP/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G डेटा रोकिएको छ"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"सेल्यूलर डेटा रोकिएको छ"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"डेटा रोकिएको छ"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"तपाईँले सेट गर्नुभएको डेटाको सीमामा पुगिएको छ। अबदेखि तपाईँ सेलुलर डेटाको प्रयोग गर्नुहुने छैन। \n\nतपाईँले प्रयोग जारी राख्नुभयो भने डेटा प्रयोगका शुल्कहरू लाग्न सक्छन्।"</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"पुनः सुरु गर्नुहोस्"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"इन्टरनेट जडान छैन"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi जडित"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 49319d1..6bcd642 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G-data zijn onderbroken"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobiele gegevens zijn onderbroken"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Gegevens zijn onderbroken"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"De ingestelde datalimiet is bereikt. Je gebruikt geen mobiele data meer.\n\nAls je hervat, kunnen er kosten voor datagebruik in rekening worden gebracht."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Hervatten"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Geen internetverbinding"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Verbonden via wifi"</string>
diff --git a/packages/SystemUI/res/values-pa-rIN/strings.xml b/packages/SystemUI/res/values-pa-rIN/strings.xml
index 082ff3a..d5b4a5d 100644
--- a/packages/SystemUI/res/values-pa-rIN/strings.xml
+++ b/packages/SystemUI/res/values-pa-rIN/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G ਡੈਟਾ ਰੁਕ ਗਿਆ ਹੈ"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"ਸੈਲਿਊਲਰ ਡੈਟਾ ਰੁਕ ਗਿਆ ਹੈ"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"ਡੈਟਾ ਰੁਕ ਗਿਆ ਹੈ"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"ਤੁਸੀਂ ਤੁਹਾਡੇ ਵੱਲੋਂ ਸੈੱਟ ਕੀਤੀ ਗਈ ਡੈਟਾ ਸੀਮਾ \'ਤੇ ਪਹੁੰਚ ਚੁੱਕੇ ਹੋ। ਤੁਸੀਂ ਹੁਣ ਸੈਲਿਊਲਰ ਡੈਟੇ ਦੀ ਵਰਤੋਂ ਨਹੀਂ ਕਰ ਰਹੇ ਹੋ।\n\nਜੇਕਰ ਤੁਸੀਂ ਮੁੜ-ਸ਼ੁਰੂ ਕਰਦੇ ਹੋ, ਤਾਂ ਡੈਟਾ ਵਰਤੋਂ ਲਈ ਖਰਚੇ ਲਾਗੂ ਹੋ ਸਕਦੇ ਹਨ।"</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"ਦੁਬਾਰਾ ਸ਼ੁਰੂ ਕਰੋ"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"ਕੋਈ ਇੰਟਰਨੈਟ ਕਨੈਕਸ਼ਨ ਨਹੀਂ"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi ਕਨੈਕਟ ਕੀਤਾ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 52bce7c..92df542 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -239,8 +239,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Transmisja danych 4G została wstrzymana"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Komórkowa transmisja danych została wstrzymana"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Transmisja danych została wstrzymana"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Osiągnięto ustawiony limit danych. Nie korzystasz już z komórkowej transmisji danych.\n\nJeśli włączysz ją ponownie, może zostać naliczona opłata za transmisję danych."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Wznów"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Brak internetu"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi: połączono"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 5ecf074..5fe2def 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -239,8 +239,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Os dados 4G foram pausados"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Os dados da rede celular foram pausados"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Os dados foram pausados"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"O limite de dados que você definiu foi atingido. Você não está mais usando os dados da rede celular.\n\nSe retomar o uso de dados, cobranças poderão ser aplicadas."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Retomar"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Sem conexão à Internet"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi conectado"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index c1e662c..ff5f566 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Dados 4G em pausa"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Dados de redes móveis em pausa"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Dados em pausa"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"O limite de dados definido foi atingido. Já não está a utilizar dados móveis.\n\nSe retomar, podem aplicar-se custos relativos à utilização de dados."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Retomar"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Sem ligação internet"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi ligado"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 5ecf074..5fe2def 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -239,8 +239,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Os dados 4G foram pausados"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Os dados da rede celular foram pausados"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Os dados foram pausados"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"O limite de dados que você definiu foi atingido. Você não está mais usando os dados da rede celular.\n\nSe retomar o uso de dados, cobranças poderão ser aplicadas."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Retomar"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Sem conexão à Internet"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi conectado"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index bedb7d0..0f7a7db 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -240,8 +240,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Conexiunea de date 4G este întreruptă"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Conexiunea de date mobile este întreruptă"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Conexiunea de date este întreruptă"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"A fost atinsă limita de date setată. Datele mobile nu mai sunt folosite \n\nDacă reluați, este posibil să se aplice taxe pentru utilizarea datelor."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Reluați"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Fără conex. internet"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi conectat"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index a155104..0e49132 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -241,8 +241,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Передача данных 4G приостановлена"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Передача мобильных данных приостановлена"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Передача данных приостановлена"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Достигнут установленный вами лимит на передачу мобильных данных.\n\nЕсли вы продолжите, может взиматься дополнительная плата."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Возобновить"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Нет интернет-подключения"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi подключено"</string>
diff --git a/packages/SystemUI/res/values-si-rLK/strings.xml b/packages/SystemUI/res/values-si-rLK/strings.xml
index 567d9e9..ff8031e 100644
--- a/packages/SystemUI/res/values-si-rLK/strings.xml
+++ b/packages/SystemUI/res/values-si-rLK/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G දත්ත විරාම කර ඇත"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"සෙලියුලර් දත්ත විරාම කර ඇත"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"දත්ත විරාම කර ඇත"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"ඔබ සැකසූ දත්ත සීමාව ළඟා වී ඇත. ඔබ තවදුරටත් සෙලියුලර් දත්ත භාවිත නොකරයි. \n\nඔබ නැවත ආරම්භ කළහොත්, දත්ත භාවිතය සඳහා ගාස්තු අදාළ විය හැකිය."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"නැවත පටන්ගන්න"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"අන්තර්ජාල සම්බන්ධතාවයක් නැත"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi සම්බන්ධිතයි"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 527a1fc..f6e43dd 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -241,8 +241,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Dátové prenosy 4G sú pozastavené"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobilné dáta sú pozastavené"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Dáta sú pozastavené"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Dosiahli ste nastavený limit dát. Už nepoužívate mobilné dátové pripojenie.\n\nAk ho však obnovíte, môžu vám byť účtované poplatky za spotrebu dát."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Znova spustiť"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Bez prip. na Internet"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi: pripojené"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 3134962..9f2f7c4 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -241,8 +241,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Prenos podatkov v omrežju 4G je zaustavljen"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Prenos mobilnih podatkov je zaustavljen"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Prenos podatkov je zaustavljen"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Dosegli ste nastavljeno omejitev porabe podatkov. Prenosa podatkov v mobilnih omrežjih ne uporabljate več.\n\nČe nadaljujete, lahko nastanejo stroški prenosa podatkov."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Nadaljuj"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Ni internetne povez."</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi povezan"</string>
diff --git a/packages/SystemUI/res/values-sq-rAL/strings.xml b/packages/SystemUI/res/values-sq-rAL/strings.xml
index afa7694..b07f551 100644
--- a/packages/SystemUI/res/values-sq-rAL/strings.xml
+++ b/packages/SystemUI/res/values-sq-rAL/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Të dhënat 4G janë ndërprerë"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Të dhënat celulare janë ndërprerë"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Të dhënat janë ndërprerë"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Kufiri i të dhënave që ke caktuar është arritur. Nuk po përdor më të dhënat celulare.\n\nNëse vazhdon, mund të zbatohen tarifa për përdorimin e të dhënave."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Rifillo"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Nuk ka lidhje interneti"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi është i lidhur"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 06fabee..35d7d52 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -238,8 +238,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G подаци су паузирани"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Мобилни подаци су паузирани"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Подаци су паузирани"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Ограничење потрошње података које сте подесили је достигнуто. Више не користите мобилне податке.\n\nАко наставите, можда ће бити наплаћени трошкови за потрошњу података."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Настави"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Нема интернет везе"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi је повезан"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 8b110c5..26b0d9f 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G-data har pausats"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobildata har pausats"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Dataanvändningen har pausats"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Den angivna datagränsen har uppnåtts. Du använder inte längre mobildata.\n\nOm du fortsätter kan avgifter för dataanvändning tillkomma."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Återuppta"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Ingen anslutning"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi-ansluten"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index be91af3..396731b 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Data ya 4G imesitishwa"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Data ya simu ya mkononi imesitishwa"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Data imesitishwa"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Umefikia kikomo cha data ulichoweka. Hutaweza kutumia tena data ya simu ya mkononi.\n\nIkiwa utaendelea, huenda ukatozwa ada za matumizi ya data."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Endelea"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Hakuna muunganisho wa mtandao"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Mtandao-hewa umeunganishwa"</string>
diff --git a/packages/SystemUI/res/values-ta-rIN/strings.xml b/packages/SystemUI/res/values-ta-rIN/strings.xml
index 799d628..465d70e 100644
--- a/packages/SystemUI/res/values-ta-rIN/strings.xml
+++ b/packages/SystemUI/res/values-ta-rIN/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G டேட்டா இடைநிறுத்தப்பட்டது"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"செல்லுலார் தரவு இடைநிறுத்தப்பட்டது"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"தரவு இடைநிறுத்தப்பட்டது"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"நீங்கள் அமைத்த தரவு வரம்பை அடைந்துவிட்டீர்கள். இப்போது செல்லுலார் தரவைப் பயன்படுத்த முடியாது.\n\nமீண்டும் தொடங்கினால், தரவுப் பயன்பாட்டிற்குக் கட்டணங்கள் விதிக்கப்படலாம்."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"மீண்டும் தொடங்கு"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"இணைய இணைப்பு இல்லை"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"வைஃபை இணைக்கப்பட்டது"</string>
diff --git a/packages/SystemUI/res/values-te-rIN/strings.xml b/packages/SystemUI/res/values-te-rIN/strings.xml
index f6e3ee2..783664d 100644
--- a/packages/SystemUI/res/values-te-rIN/strings.xml
+++ b/packages/SystemUI/res/values-te-rIN/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G డేటా పాజ్ చేయబడింది"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"సెల్యులార్ డేటా పాజ్ చేయబడింది"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"డేటా పాజ్ చేయబడింది"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"మీరు సెట్ చేసిన డేటా పరిమితిని చేరుకున్నారు. మీరు ఇప్పుడు సెల్యులార్ డేటాను ఉపయోగించడం లేదు.\n\nమీరు పునఃప్రారంభిస్తే, డేటా వినియోగానికి ఛార్జీలు వర్తించవచ్చు."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"పునఃప్రారంభించు"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"ఇంటర్నెట్ కనెక్షన్ లేదు"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi కనెక్ట్ చేయబడింది"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 0482e3b..1fbd6f4 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"หยุดการใช้ข้อมูล 4G ชั่วคราวแล้ว"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"หยุดการใช้ข้อมูลมือถือชั่วคราวแล้ว"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"หยุดการใช้ข้อมูลชั่วคราวแล้ว"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"คุณใช้อินเทอร์เน็ตเกินปริมาณที่กำหนดไว้แล้ว คุณจะไม่สามารถใช้ข้อมูลเครือข่ายมือถืออีก\n\nหากใช้ต่อ อาจมีค่าบริการสำหรับปริมาณการใช้อินเทอร์เน็ต"</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"ทำต่อ"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"ไม่มีอินเทอร์เน็ต"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"เชื่อมต่อ WiFi แล้ว"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 9b104f5..acaf86c 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Naka-pause ang 4G data"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Naka-pause ang cellular data"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Naka-pause ang data"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Naabot na ang limitasyon sa data na itinakda mo. Hindi ka na gumagamit ng cellular data.\n\nKung magpapatuloy ka, maaari kang masingil para sa paggamit ng data."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Ipagpatuloy"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Walang koneksyon sa Internet"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"nakakonekta ang Wi-Fi"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index e655cd6..b4dc25d 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G veri kullanımı duraklatıldı"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Hücresel veri kullanımı duraklatıldı"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Veri kullanımı duraklatıldı"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Ayarladığınız veri limitine ulaşıldı. Artık hücresel verilerinizi kullanmıyorsunuz.\n\nHücresel veri kullanımını devam ettirirseniz veri kullanım ücretleri ödemeniz gerekebilir."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Devam ettir"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"İnternet bağlantısı yok"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Kablosuz bağlandı"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index eccc4a2..4d2b6c39 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -241,8 +241,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Передавання даних 4G призупинено"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Передавання мобільних даних призупинено"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Передавання даних призупинено"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Ви досягнули вказаного ліміту даних. Мобільний трафік вимкнено.\n\nЯкщо продовжите, може стягуватися плата за використання трафіку."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Відновити"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Немає з’єднання"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi під’єднано"</string>
diff --git a/packages/SystemUI/res/values-ur-rPK/strings.xml b/packages/SystemUI/res/values-ur-rPK/strings.xml
index dc177d1..cbe3d23 100644
--- a/packages/SystemUI/res/values-ur-rPK/strings.xml
+++ b/packages/SystemUI/res/values-ur-rPK/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"‏4G ڈیٹا موقوف کر دیا گیا"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"سیلولر ڈیٹا موقوف کر دیا گیا"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"ڈیٹا موقوف کر دیا گیا"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"آپ کی سیٹ کردہ ڈیٹا کی حد پوری ہو گئی ہے۔ آپ اب سیلولر ڈیٹا استعمال نہیں کر رہے۔\n\nاگر آپ دوبارہ شروع کرتے ہیں تو ڈیٹا کے استعمال کے چارجز لاگو ہو سکتے ہیں۔"</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"دوبارہ شروع کریں"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"کوئی انٹرنیٹ کنکشن نہیں"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"‏Wi-Fi مربوط ہے"</string>
diff --git a/packages/SystemUI/res/values-uz-rUZ/strings.xml b/packages/SystemUI/res/values-uz-rUZ/strings.xml
index 32fb303..3f1bb9b 100644
--- a/packages/SystemUI/res/values-uz-rUZ/strings.xml
+++ b/packages/SystemUI/res/values-uz-rUZ/strings.xml
@@ -239,8 +239,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G internet to‘xtatib qo‘yildi"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Mobil internetdan foydalanish to‘xtatib qo‘yildi"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Internetdan foydalanish to‘xtatib qo‘yildi"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"O‘rnatilgan trafik sarflab bo‘lindi. Endi mobil internetdan foydalana olmaysiz.\n\nDavom ettiradigan bo‘lsangiz, trafik uchun to‘lov olinishi mumkin."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Davom etish"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Internetga ulanmagan"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi ulandi"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 4146796..ceeaf18 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"Đã tạm dừng dữ liệu 4G"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Đã tạm dừng dữ liệu di động"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Đã tạm dừng dữ liệu"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Đã đạt đến giới hạn dữ liệu mà bạn đặt. Bạn hiện không còn sử dụng dữ liệu di động.\n\nNếu tiếp tục, bạn có thể bị tính phí khi sử dụng dữ liệu."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Tiếp tục"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Ko có k.nối Internet"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Đã kết nối Wi-Fi"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 30f6245..1217318 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G 数据网络已暂停使用"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"移动数据网络已暂停使用"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"数据网络已暂停使用"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"您的数据用量已达到设置的上限。您无法再使用移动数据网络。\n\n如果您继续操作,可能需要支付相应的数据流量费用。"</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"恢复"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"未连接互联网"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"已连接到WLAN网络"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index f00a30b..bb51ebe 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -239,8 +239,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"已暫停 4G 數據"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"已暫停流動數據"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"已暫停使用數據"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"已達到您設定的數據上限。系統將停止使用流動數據網絡。\n\n如果您恢復使用流動數據網絡,可能需要支付數據費用。"</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"恢復"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"沒有互聯網連線"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi 已連線"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 34d12ff..ac6b002 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"已暫停 4G 數據連線"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"已暫停行動數據連線"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"已暫停數據連線"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"你的數據用量已達設定的用量上限,因此系統已停止使用行動數據連線。\n\n如果你繼續使用行動數據連線,可能需要支付相關的數據傳輸費用。"</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"恢復連線"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"沒有網際網路連線"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"Wi-Fi 已連線"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 1b2794e..78e22f1 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -237,8 +237,7 @@
     <string name="data_usage_disabled_dialog_4g_title" msgid="1601769736881078016">"4G idatha imisiwe"</string>
     <string name="data_usage_disabled_dialog_mobile_title" msgid="4651001290947318931">"Idatha yeselula imisiwe"</string>
     <string name="data_usage_disabled_dialog_title" msgid="3932437232199671967">"Idatha imisiwe"</string>
-    <!-- no translation found for data_usage_disabled_dialog (1841738975235283398) -->
-    <skip />
+    <string name="data_usage_disabled_dialog" msgid="1841738975235283398">"Umkhawulo wedatha owusethayo ufikiwe. Awusasebenzisi idatha yeselula.\n\nUma uqalisa kabusha, izindleko zingasebenza ekusetshenzisweni kwedatha."</string>
     <string name="data_usage_disabled_dialog_enable" msgid="1412395410306390593">"Qalisa kabusha"</string>
     <string name="status_bar_settings_signal_meter_disconnected" msgid="1940231521274147771">"Alukho uxhumano lwe-Inthanethi"</string>
     <string name="status_bar_settings_signal_meter_wifi_nossid" msgid="6557486452774597820">"I-Wi-Fi ixhunyiwe"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index 076b5bc..e1d6a94 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -31,10 +31,9 @@
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.Handler;
+import android.os.Looper;
 import android.provider.Settings;
-
 import com.android.systemui.statusbar.policy.BatteryController;
 
 public class BatteryMeterDrawable extends Drawable implements
@@ -92,18 +91,17 @@
     private int mLightModeBackgroundColor;
     private int mLightModeFillColor;
 
-    private final SettingObserver mSettingObserver = new SettingObserver();
+    private final SettingObserver mSettingObserver;
 
     private final Context mContext;
-    private final Handler mHandler;
 
     private int mLevel = -1;
     private boolean mPluggedIn;
     private boolean mListening;
 
-    public BatteryMeterDrawable(Context context, Handler handler, int frameColor) {
+    public BatteryMeterDrawable(Context context, int frameColor) {
         mContext = context;
-        mHandler = handler;
+        mSettingObserver = new SettingObserver(new Handler(mContext.getMainLooper()));
         final Resources res = context.getResources();
         TypedArray levels = res.obtainTypedArray(R.array.batterymeter_color_levels);
         TypedArray colors = res.obtainTypedArray(R.array.batterymeter_color_values);
@@ -199,12 +197,7 @@
     }
 
     private void postInvalidate() {
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                invalidateSelf();
-            }
-        });
+        scheduleSelf(this::invalidateSelf, 0);
     }
 
     public void setBatteryController(BatteryController batteryController) {
@@ -506,8 +499,8 @@
     }
 
     private final class SettingObserver extends ContentObserver {
-        public SettingObserver() {
-            super(new Handler());
+        public SettingObserver(Handler handler) {
+            super(handler);
         }
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index d8b95cc..4f3ffde 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -49,7 +49,7 @@
                 defStyle, 0);
         final int frameColor = atts.getColor(R.styleable.BatteryMeterView_frameColor,
                 context.getColor(R.color.batterymeter_frame_color));
-        mDrawable = new BatteryMeterDrawable(context, new Handler(), frameColor);
+        mDrawable = new BatteryMeterDrawable(context, frameColor);
         atts.recycle();
 
         mSlotBattery = context.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index afedbe3..1c242e9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -88,9 +88,9 @@
         if (mListening == listening) return;
         mListening = listening;
         if (mListening) {
-            mPages.get(mPosition).setListening(listening);
+            setPageListening(mPosition, true);
             if (mOffPage) {
-                mPages.get(mPosition + 1).setListening(listening);
+                setPageListening(mPosition + 1, true);
             }
         } else {
             // Make sure no pages are listening.
@@ -131,6 +131,9 @@
 
     private void setPageListening(int position, boolean listening) {
         if (position >= mPages.size()) return;
+        if (isLayoutRtl()) {
+            position = mPages.size() - 1 - position;
+        }
         mPages.get(position).setListening(listening);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
index b61a81c..d89fbfd3c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java
@@ -118,7 +118,7 @@
             @Override
             public Drawable getDrawable(Context context) {
                 BatteryMeterDrawable drawable =
-                        new BatteryMeterDrawable(context, new Handler(Looper.getMainLooper()),
+                        new BatteryMeterDrawable(context,
                         context.getColor(R.color.batterymeter_frame_color));
                 drawable.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
                 drawable.onPowerSaveChanged(mPowerSave);
@@ -165,7 +165,7 @@
     private final class BatteryDetail implements DetailAdapter, OnClickListener,
             OnAttachStateChangeListener {
         private final BatteryMeterDrawable mDrawable = new BatteryMeterDrawable(mHost.getContext(),
-                new Handler(), mHost.getContext().getColor(R.color.batterymeter_frame_color));
+                mHost.getContext().getColor(R.color.batterymeter_frame_color));
         private View mCurrentView;
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index a7132e5..7a1ea47 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -161,7 +161,7 @@
             "com.android.systemui.statusbar.banner_action_cancel";
     private static final String BANNER_ACTION_SETUP =
             "com.android.systemui.statusbar.banner_action_setup";
-    private static final String WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION
+    private static final String NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION
             = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
 
     protected CommandQueue mCommandQueue;
@@ -217,14 +217,14 @@
     protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
 
     // public mode, private notifications, etc
-    private boolean mLockscreenPublicMode = false;
+    private final SparseBooleanArray mLockscreenPublicMode = new SparseBooleanArray();
     private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
     private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
 
     private UserManager mUserManager;
     private int mDensity;
 
-    private KeyguardManager mKeyguardManager;
+    protected KeyguardManager mKeyguardManager;
     private LockPatternUtils mLockPatternUtils;
 
     // UI-specific methods
@@ -465,11 +465,11 @@
             row.setUserExpanded(true);
 
             if (!mAllowLockscreenRemoteInput) {
-                if (isLockscreenPublicMode()) {
+                final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
+                if (isLockscreenPublicMode(userId)) {
                     onLockedRemoteInput(row, view);
                     return true;
                 }
-                final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
                 if (mUserManager.getUserInfo(userId).isManagedProfile()
                         && mKeyguardManager.isDeviceLocked(userId)) {
                     onLockedWorkRemoteInput(userId, row, view);
@@ -560,7 +560,7 @@
 
                     );
                 }
-            } else if (WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION.equals(action)) {
+            } else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) {
                 final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
                 final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
                 if (intentSender != null) {
@@ -577,7 +577,6 @@
                         /* ignore */
                     }
                 }
-                onWorkChallengeUnlocked();
             }
         }
     };
@@ -585,12 +584,18 @@
     private final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
+            final String action = intent.getAction();
+            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+
             if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) &&
                     isCurrentProfile(getSendingUserId())) {
                 mUsersAllowingPrivateNotifications.clear();
                 updateLockscreenNotificationSetting();
                 updateNotifications();
+            } else if (Intent.ACTION_DEVICE_LOCKED_CHANGED.equals(action)) {
+                if (userId != mCurrentUserId && isCurrentProfile(userId)) {
+                    onWorkChallengeChanged();
+                }
             }
         }
     };
@@ -815,7 +820,7 @@
         mContext.registerReceiver(mBroadcastReceiver, filter);
 
         IntentFilter internalFilter = new IntentFilter();
-        internalFilter.addAction(WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION);
+        internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
         internalFilter.addAction(BANNER_ACTION_CANCEL);
         internalFilter.addAction(BANNER_ACTION_SETUP);
         mContext.registerReceiver(mBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
@@ -823,6 +828,7 @@
         IntentFilter allUsersFilter = new IntentFilter();
         allUsersFilter.addAction(
                 DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
+        allUsersFilter.addAction(Intent.ACTION_DEVICE_LOCKED_CHANGED);
         mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
                 null, null);
         updateCurrentProfilesCache();
@@ -1126,9 +1132,10 @@
             @Override
             public void onClick(View v) {
                 // If the user has security enabled, show challenge if the setting is changed.
-                if (guts.hasImportanceChanged() && isLockscreenPublicMode() &&
-                        (mState == StatusBarState.KEYGUARD
-                        || mState == StatusBarState.SHADE_LOCKED)) {
+                if (guts.hasImportanceChanged()
+                            && isLockscreenPublicMode(sbn.getUser().getIdentifier())
+                            && (mState == StatusBarState.KEYGUARD
+                                    || mState == StatusBarState.SHADE_LOCKED)) {
                     OnDismissAction dismissAction = new OnDismissAction() {
                         @Override
                         public boolean onDismiss() {
@@ -1430,15 +1437,15 @@
     /**
      * Save the current "public" (locked and secure) state of the lockscreen.
      */
-    public void setLockscreenPublicMode(boolean publicMode) {
-        mLockscreenPublicMode = publicMode;
+    public void setLockscreenPublicMode(boolean publicMode, int userId) {
+        mLockscreenPublicMode.put(userId, publicMode);
     }
 
-    public boolean isLockscreenPublicMode() {
-        return mLockscreenPublicMode;
+    public boolean isLockscreenPublicMode(int userId) {
+        return mLockscreenPublicMode.get(userId, false);
     }
 
-    protected void onWorkChallengeUnlocked() {}
+    protected void onWorkChallengeChanged() {}
 
     /**
      * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
@@ -1496,8 +1503,9 @@
      * If so, notifications should be hidden.
      */
     @Override  // NotificationData.Environment
-    public boolean shouldHideNotifications(int userid) {
-        return isLockscreenPublicMode() && !userAllowsNotificationsInPublic(userid);
+    public boolean shouldHideNotifications(int userId) {
+        return isLockscreenPublicMode(mCurrentUserId) && !userAllowsNotificationsInPublic(userId)
+                || (userId != mCurrentUserId && shouldHideNotifications(mCurrentUserId));
     }
 
     /**
@@ -1506,7 +1514,7 @@
      */
     @Override // NotificationDate.Environment
     public boolean shouldHideNotifications(String key) {
-        return isLockscreenPublicMode()
+        return isLockscreenPublicMode(mCurrentUserId)
                 && mNotificationData.getVisibilityOverride(key) == Notification.VISIBILITY_SECRET;
     }
 
@@ -1514,8 +1522,8 @@
      * Returns true if we're on a secure lockscreen.
      */
     @Override  // NotificationData.Environment
-    public boolean onSecureLockScreen() {
-        return isLockscreenPublicMode();
+    public boolean isSecurelyLocked(int userId) {
+        return isLockscreenPublicMode(userId);
     }
 
     public void onNotificationClear(StatusBarNotification notification) {
@@ -2076,8 +2084,7 @@
         if (newIntent == null) {
             return false;
         }
-        final Intent callBackIntent = new Intent(
-                WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION);
+        final Intent callBackIntent = new Intent(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
         callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender);
         callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey);
         callBackIntent.setPackage(mContext.getPackageName());
@@ -2276,14 +2283,16 @@
                 entry.row.setOnKeyguard(false);
                 entry.row.setSystemExpanded(visibleNotifications == 0 && !childNotification);
             }
+            int userId = entry.notification.getUserId();
             boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
                     entry.notification) && !entry.row.isRemoved();
             boolean childWithVisibleSummary = childNotification
                     && mGroupManager.getGroupSummary(entry.notification).getVisibility()
                     == View.VISIBLE;
             boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
-            if (suppressedSummary || (isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
-                    (onKeyguard && !childWithVisibleSummary
+            if (suppressedSummary
+                    || (isLockscreenPublicMode(userId) && !mShowLockscreenNotifications)
+                    || (onKeyguard && !childWithVisibleSummary
                             && (visibleNotifications >= maxNotifications || !showOnKeyguard))) {
                 entry.row.setVisibility(View.GONE);
                 if (onKeyguard && showOnKeyguard && !childNotification && !suppressedSummary) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index b6e54af..bae16f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -380,7 +380,7 @@
             return true;
         }
 
-        if (mEnvironment.onSecureLockScreen() &&
+        if (mEnvironment.isSecurelyLocked(sbn.getUserId()) &&
                 (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
                         || mEnvironment.shouldHideNotifications(sbn.getUserId())
                         || mEnvironment.shouldHideNotifications(sbn.getKey()))) {
@@ -463,7 +463,7 @@
      * Provides access to keyguard state and user settings dependent data.
      */
     public interface Environment {
-        public boolean onSecureLockScreen();
+        public boolean isSecurelyLocked(int userId);
         public boolean shouldHideNotifications(int userid);
         public boolean shouldHideNotifications(String key);
         public boolean isDeviceProvisioned();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
index 95cb672..f6fe176 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ButtonDispatcher.java
@@ -14,11 +14,11 @@
 
 package com.android.systemui.statusbar.phone;
 
-import android.annotation.DrawableRes;
-import android.annotation.Nullable;
 import android.graphics.drawable.Drawable;
 import android.view.View;
 
+import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
+
 import java.util.ArrayList;
 
 /**
@@ -186,18 +186,4 @@
         }
     }
 
-    /**
-     * Interface for button actions.
-     */
-    public interface ButtonInterface {
-        void setImageResource(@DrawableRes int resId);
-
-        void setImageDrawable(@Nullable Drawable drawable);
-
-        void abortCurrentGesture();
-
-        void setLandscape(boolean landscape);
-
-        void setCarMode(boolean carMode);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 0a391eb..4270147 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -61,6 +61,11 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.assist.AssistManager;
+import com.android.systemui.plugins.IntentButtonProvider;
+import com.android.systemui.plugins.IntentButtonProvider.IntentButton;
+import com.android.systemui.plugins.IntentButtonProvider.IntentButton.IconState;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
 import com.android.systemui.plugins.qs.QSContainer.ActivityStarter;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
@@ -86,6 +91,11 @@
     public static final String EXTRA_CAMERA_LAUNCH_SOURCE
             = "com.android.systemui.camera_launch_source";
 
+    private static final String LEFT_BUTTON_PLUGIN
+            = "com.android.systemui.action.PLUGIN_LOCKSCREEN_LEFT_BUTTON";
+    private static final String RIGHT_BUTTON_PLUGIN
+            = "com.android.systemui.action.PLUGIN_LOCKSCREEN_RIGHT_BUTTON";
+
     private static final Intent SECURE_CAMERA_INTENT =
             new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
                     .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
@@ -95,7 +105,7 @@
     private static final int DOZE_ANIMATION_STAGGER_DELAY = 48;
     private static final int DOZE_ANIMATION_ELEMENT_DURATION = 250;
 
-    private KeyguardAffordanceView mCameraImageView;
+    private KeyguardAffordanceView mRightAffordanceView;
     private KeyguardAffordanceView mLeftAffordanceView;
     private LockIcon mLockIcon;
     private TextView mIndicationText;
@@ -132,6 +142,9 @@
     private boolean mLeftIsVoiceAssist;
     private AssistManager mAssistManager;
 
+    private IntentButton mRightButton = new DefaultRightButton();
+    private IntentButton mLeftButton = new DefaultLeftButton();
+
     public KeyguardBottomAreaView(Context context) {
         this(context, null);
     }
@@ -156,7 +169,7 @@
             String label = null;
             if (host == mLockIcon) {
                 label = getResources().getString(R.string.unlock_label);
-            } else if (host == mCameraImageView) {
+            } else if (host == mRightAffordanceView) {
                 label = getResources().getString(R.string.camera_label);
             } else if (host == mLeftAffordanceView) {
                 if (mLeftIsVoiceAssist) {
@@ -175,7 +188,7 @@
                     mPhoneStatusBar.animateCollapsePanels(
                             CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */);
                     return true;
-                } else if (host == mCameraImageView) {
+                } else if (host == mRightAffordanceView) {
                     launchCamera(CAMERA_LAUNCH_SOURCE_AFFORDANCE);
                     return true;
                 } else if (host == mLeftAffordanceView) {
@@ -192,7 +205,7 @@
         super.onFinishInflate();
         mLockPatternUtils = new LockPatternUtils(mContext);
         mPreviewContainer = (ViewGroup) findViewById(R.id.preview_container);
-        mCameraImageView = (KeyguardAffordanceView) findViewById(R.id.camera_button);
+        mRightAffordanceView = (KeyguardAffordanceView) findViewById(R.id.camera_button);
         mLeftAffordanceView = (KeyguardAffordanceView) findViewById(R.id.left_button);
         mLockIcon = (LockIcon) findViewById(R.id.lock_icon);
         mIndicationText = (TextView) findViewById(R.id.keyguard_indication_text);
@@ -207,15 +220,31 @@
         inflateCameraPreview();
         mLockIcon.setOnClickListener(this);
         mLockIcon.setOnLongClickListener(this);
-        mCameraImageView.setOnClickListener(this);
+        mRightAffordanceView.setOnClickListener(this);
         mLeftAffordanceView.setOnClickListener(this);
         initAccessibility();
     }
 
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        PluginManager.getInstance(getContext()).addPluginListener(RIGHT_BUTTON_PLUGIN,
+                mRightListener, IntentButtonProvider.VERSION, false /* Only allow one */);
+        PluginManager.getInstance(getContext()).addPluginListener(LEFT_BUTTON_PLUGIN,
+                mLeftListener, IntentButtonProvider.VERSION, false /* Only allow one */);
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        PluginManager.getInstance(getContext()).removePluginListener(mRightListener);
+        PluginManager.getInstance(getContext()).removePluginListener(mLeftListener);
+    }
+
     private void initAccessibility() {
         mLockIcon.setAccessibilityDelegate(mAccessibilityDelegate);
         mLeftAffordanceView.setAccessibilityDelegate(mAccessibilityDelegate);
-        mCameraImageView.setAccessibilityDelegate(mAccessibilityDelegate);
+        mRightAffordanceView.setAccessibilityDelegate(mAccessibilityDelegate);
     }
 
     @Override
@@ -234,11 +263,11 @@
                 getResources().getDimensionPixelSize(
                         com.android.internal.R.dimen.text_size_small_material));
 
-        ViewGroup.LayoutParams lp = mCameraImageView.getLayoutParams();
+        ViewGroup.LayoutParams lp = mRightAffordanceView.getLayoutParams();
         lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width);
         lp.height = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_height);
-        mCameraImageView.setLayoutParams(lp);
-        mCameraImageView.setImageDrawable(mContext.getDrawable(R.drawable.ic_camera_alt_24dp));
+        mRightAffordanceView.setLayoutParams(lp);
+        updateRightAffordanceIcon();
 
         lp = mLockIcon.getLayoutParams();
         lp.width = getResources().getDimensionPixelSize(R.dimen.keyguard_affordance_width);
@@ -253,6 +282,13 @@
         updateLeftAffordanceIcon();
     }
 
+    private void updateRightAffordanceIcon() {
+        IconState state = mRightButton.getIcon();
+        mRightAffordanceView.setVisibility(state.isVisible ? View.VISIBLE : View.GONE);
+        mRightAffordanceView.setImageDrawable(state.drawable);
+        mRightAffordanceView.setContentDescription(state.contentDescription);
+    }
+
     public void setActivityStarter(ActivityStarter activityStarter) {
         mActivityStarter = activityStarter;
     }
@@ -279,11 +315,7 @@
     }
 
     private Intent getCameraIntent() {
-        KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
-        boolean canSkipBouncer = updateMonitor.getUserCanSkipBouncer(
-                KeyguardUpdateMonitor.getCurrentUser());
-        boolean secure = mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser());
-        return (secure && !canSkipBouncer) ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
+        return mRightButton.getIntent();
     }
 
     /**
@@ -296,33 +328,19 @@
     }
 
     private void updateCameraVisibility() {
-        if (mCameraImageView == null) {
+        if (mRightAffordanceView == null) {
             // Things are not set up yet; reply hazy, ask again later
             return;
         }
-        ResolveInfo resolved = resolveCameraIntent();
-        boolean visible = !isCameraDisabledByDpm() && resolved != null
-                && getResources().getBoolean(R.bool.config_keyguardShowCameraAffordance)
-                && mUserSetupComplete;
-        mCameraImageView.setVisibility(visible ? View.VISIBLE : View.GONE);
+        mRightAffordanceView.setVisibility(mRightButton.getIcon().isVisible
+                ? View.VISIBLE : View.GONE);
     }
 
     private void updateLeftAffordanceIcon() {
-        mLeftIsVoiceAssist = canLaunchVoiceAssist();
-        int drawableId;
-        int contentDescription;
-        boolean visible = mUserSetupComplete;
-        if (mLeftIsVoiceAssist) {
-            drawableId = R.drawable.ic_mic_26dp;
-            contentDescription = R.string.accessibility_voice_assist_button;
-        } else {
-            visible &= isPhoneVisible();
-            drawableId = R.drawable.ic_phone_24dp;
-            contentDescription = R.string.accessibility_phone_button;
-        }
-        mLeftAffordanceView.setVisibility(visible ? View.VISIBLE : View.GONE);
-        mLeftAffordanceView.setImageDrawable(mContext.getDrawable(drawableId));
-        mLeftAffordanceView.setContentDescription(mContext.getString(contentDescription));
+        IconState state = mLeftButton.getIcon();
+        mLeftAffordanceView.setVisibility(state.isVisible ? View.VISIBLE : View.GONE);
+        mLeftAffordanceView.setImageDrawable(state.drawable);
+        mLeftAffordanceView.setContentDescription(state.contentDescription);
     }
 
     public boolean isLeftVoiceAssist() {
@@ -363,16 +381,16 @@
 
     @Override
     public void onStateChanged(boolean accessibilityEnabled, boolean touchExplorationEnabled) {
-        mCameraImageView.setClickable(touchExplorationEnabled);
+        mRightAffordanceView.setClickable(touchExplorationEnabled);
         mLeftAffordanceView.setClickable(touchExplorationEnabled);
-        mCameraImageView.setFocusable(accessibilityEnabled);
+        mRightAffordanceView.setFocusable(accessibilityEnabled);
         mLeftAffordanceView.setFocusable(accessibilityEnabled);
         mLockIcon.update();
     }
 
     @Override
     public void onClick(View v) {
-        if (v == mCameraImageView) {
+        if (v == mRightAffordanceView) {
             launchCamera(CAMERA_LAUNCH_SOURCE_AFFORDANCE);
         } else if (v == mLeftAffordanceView) {
             launchLeftAffordance();
@@ -541,7 +559,7 @@
                 }
             });
         } else {
-            mActivityStarter.startActivity(PHONE_INTENT, false /* dismissShade */);
+            mActivityStarter.startActivity(mLeftButton.getIntent(), false /* dismissShade */);
         }
     }
 
@@ -560,7 +578,7 @@
     }
 
     public KeyguardAffordanceView getRightView() {
-        return mCameraImageView;
+        return mRightAffordanceView;
     }
 
     public View getLeftPreview() {
@@ -613,7 +631,7 @@
             mLeftPreview = mPreviewInflater.inflatePreviewFromService(
                     mAssistManager.getVoiceInteractorComponentName());
         } else {
-            mLeftPreview = mPreviewInflater.inflatePreview(PHONE_INTENT);
+            mLeftPreview = mPreviewInflater.inflatePreview(mLeftButton.getIntent());
         }
         if (mLeftPreview != null) {
             mPreviewContainer.addView(mLeftPreview);
@@ -629,8 +647,8 @@
         }
         startFinishDozeAnimationElement(mLockIcon, delay);
         delay += DOZE_ANIMATION_STAGGER_DELAY;
-        if (mCameraImageView.getVisibility() == View.VISIBLE) {
-            startFinishDozeAnimationElement(mCameraImageView, delay);
+        if (mRightAffordanceView.getVisibility() == View.VISIBLE) {
+            startFinishDozeAnimationElement(mRightAffordanceView, delay);
         }
         mIndicationText.setAlpha(0f);
         mIndicationText.animate()
@@ -664,46 +682,46 @@
 
     private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
             new KeyguardUpdateMonitorCallback() {
-        @Override
-        public void onUserSwitchComplete(int userId) {
-            updateCameraVisibility();
-        }
+                @Override
+                public void onUserSwitchComplete(int userId) {
+                    updateCameraVisibility();
+                }
 
-        @Override
-        public void onStartedWakingUp() {
-            mLockIcon.setDeviceInteractive(true);
-        }
+                @Override
+                public void onStartedWakingUp() {
+                    mLockIcon.setDeviceInteractive(true);
+                }
 
-        @Override
-        public void onFinishedGoingToSleep(int why) {
-            mLockIcon.setDeviceInteractive(false);
-        }
+                @Override
+                public void onFinishedGoingToSleep(int why) {
+                    mLockIcon.setDeviceInteractive(false);
+                }
 
-        @Override
-        public void onScreenTurnedOn() {
-            mLockIcon.setScreenOn(true);
-        }
+                @Override
+                public void onScreenTurnedOn() {
+                    mLockIcon.setScreenOn(true);
+                }
 
-        @Override
-        public void onScreenTurnedOff() {
-            mLockIcon.setScreenOn(false);
-        }
+                @Override
+                public void onScreenTurnedOff() {
+                    mLockIcon.setScreenOn(false);
+                }
 
-        @Override
-        public void onKeyguardVisibilityChanged(boolean showing) {
-            mLockIcon.update();
-        }
+                @Override
+                public void onKeyguardVisibilityChanged(boolean showing) {
+                    mLockIcon.update();
+                }
 
-        @Override
-        public void onFingerprintRunningStateChanged(boolean running) {
-            mLockIcon.update();
-        }
+                @Override
+                public void onFingerprintRunningStateChanged(boolean running) {
+                    mLockIcon.update();
+                }
 
-        @Override
-        public void onStrongAuthStateChanged(int userId) {
-            mLockIcon.update();
-        }
-    };
+                @Override
+                public void onStrongAuthStateChanged(int userId) {
+                    mLockIcon.update();
+                }
+            };
 
     public void setKeyguardIndicationController(
             KeyguardIndicationController keyguardIndicationController) {
@@ -724,4 +742,96 @@
         updateLeftAffordance();
         inflateCameraPreview();
     }
+
+    private void setRightButton(IntentButton button) {
+        mRightButton = button;
+        updateRightAffordanceIcon();
+        updateCameraVisibility();
+        inflateCameraPreview();
+    }
+
+    private void setLeftButton(IntentButton button) {
+        mLeftButton = button;
+        mLeftIsVoiceAssist = false;
+        updateLeftAffordance();
+    }
+
+    private final PluginListener<IntentButtonProvider> mRightListener =
+            new PluginListener<IntentButtonProvider>() {
+        @Override
+        public void onPluginConnected(IntentButtonProvider plugin) {
+            setRightButton(plugin.getIntentButton());
+        }
+
+        @Override
+        public void onPluginDisconnected(IntentButtonProvider plugin) {
+            setRightButton(new DefaultRightButton());
+        }
+    };
+
+    private final PluginListener<IntentButtonProvider> mLeftListener =
+            new PluginListener<IntentButtonProvider>() {
+        @Override
+        public void onPluginConnected(IntentButtonProvider plugin) {
+            setLeftButton(plugin.getIntentButton());
+        }
+
+        @Override
+        public void onPluginDisconnected(IntentButtonProvider plugin) {
+            setLeftButton(new DefaultLeftButton());
+        }
+    };
+
+    private class DefaultLeftButton implements IntentButton {
+
+        private IconState mIconState = new IconState();
+
+        @Override
+        public IconState getIcon() {
+            mLeftIsVoiceAssist = canLaunchVoiceAssist();
+            if (mLeftIsVoiceAssist) {
+                mIconState.isVisible = mUserSetupComplete;
+                mIconState.drawable = mContext.getDrawable(R.drawable.ic_mic_26dp);
+                mIconState.contentDescription = mContext.getString(
+                        R.string.accessibility_voice_assist_button);
+            } else {
+                mIconState.isVisible = mUserSetupComplete && isPhoneVisible();
+                mIconState.drawable = mContext.getDrawable(R.drawable.ic_phone_24dp);
+                mIconState.contentDescription = mContext.getString(
+                        R.string.accessibility_phone_button);
+            }
+            return mIconState;
+        }
+
+        @Override
+        public Intent getIntent() {
+            return PHONE_INTENT;
+        }
+    }
+
+    private class DefaultRightButton implements IntentButton {
+
+        private IconState mIconState = new IconState();
+
+        @Override
+        public IconState getIcon() {
+            ResolveInfo resolved = resolveCameraIntent();
+            mIconState.isVisible = !isCameraDisabledByDpm() && resolved != null
+                    && getResources().getBoolean(R.bool.config_keyguardShowCameraAffordance)
+                    && mUserSetupComplete;
+            mIconState.drawable = mContext.getDrawable(R.drawable.ic_camera_alt_24dp);
+            mIconState.contentDescription =
+                    mContext.getString(R.string.accessibility_camera_button);
+            return mIconState;
+        }
+
+        @Override
+        public Intent getIntent() {
+            KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
+            boolean canSkipBouncer = updateMonitor.getUserCanSkipBouncer(
+                    KeyguardUpdateMonitor.getCurrentUser());
+            boolean secure = mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser());
+            return (secure && !canSkipBouncer) ? SECURE_CAMERA_INTENT : INSECURE_CAMERA_INTENT;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index 06c8b68..c420927 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -28,12 +28,19 @@
 import android.widget.Space;
 
 import com.android.systemui.R;
+import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
+import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider;
 import com.android.systemui.statusbar.policy.KeyButtonView;
 import com.android.systemui.tuner.TunerService;
+import com.android.systemui.tuner.TunerService.Tunable;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Objects;
 
-public class NavigationBarInflaterView extends FrameLayout implements TunerService.Tunable {
+public class NavigationBarInflaterView extends FrameLayout
+        implements Tunable, PluginListener<NavBarButtonProvider> {
 
     private static final String TAG = "NavBarInflater";
 
@@ -57,6 +64,8 @@
     public static final String KEY_IMAGE_DELIM = ":";
     public static final String KEY_CODE_END = ")";
 
+    private final List<NavBarButtonProvider> mPlugins = new ArrayList<>();
+
     protected LayoutInflater mLayoutInflater;
     protected LayoutInflater mLandscapeInflater;
     private int mDensity;
@@ -129,6 +138,8 @@
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
         TunerService.get(getContext()).addTunable(this, NAV_BAR_VIEWS);
+        PluginManager.getInstance(getContext()).addPluginListener(NavBarButtonProvider.ACTION, this,
+                NavBarButtonProvider.VERSION, true /* Allow multiple */);
     }
 
     @Override
@@ -240,8 +251,36 @@
             int indexInParent) {
         LayoutInflater inflater = landscape ? mLandscapeInflater : mLayoutInflater;
         float size = extractSize(buttonSpec);
-        String button = extractButton(buttonSpec);
+        View v = createView(buttonSpec, parent, inflater, landscape);
+        if (v == null) return null;
+
+        if (size != 0) {
+            ViewGroup.LayoutParams params = v.getLayoutParams();
+            params.width = (int) (params.width * size);
+        }
+        parent.addView(v);
+        addToDispatchers(v, landscape);
+        View lastView = landscape ? mLastRot90 : mLastRot0;
+        if (lastView != null) {
+            v.setAccessibilityTraversalAfter(lastView.getId());
+        }
+        if (landscape) {
+            mLastRot90 = v;
+        } else {
+            mLastRot0 = v;
+        }
+        return v;
+    }
+
+    private View createView(String buttonSpec, ViewGroup parent, LayoutInflater inflater,
+            boolean landscape) {
         View v = null;
+        String button = extractButton(buttonSpec);
+        // Let plugins go first so they can override a standard view if they want.
+        for (NavBarButtonProvider provider : mPlugins) {
+            v = provider.createView(buttonSpec, parent);
+            if (v != null) return v;
+        }
         if (HOME.equals(button)) {
             v = inflater.inflate(R.layout.home, parent, false);
             if (landscape && isSw600Dp()) {
@@ -271,24 +310,6 @@
             if (uri != null) {
                 ((KeyButtonView) v).loadAsync(uri);
             }
-        } else {
-            return null;
-        }
-
-        if (size != 0) {
-            ViewGroup.LayoutParams params = v.getLayoutParams();
-            params.width = (int) (params.width * size);
-        }
-        parent.addView(v);
-        addToDispatchers(v, landscape);
-        View lastView = landscape ? mLastRot90 : mLastRot0;
-        if (lastView != null) {
-            v.setAccessibilityTraversalAfter(lastView.getId());
-        }
-        if (landscape) {
-            mLastRot90 = v;
-        } else {
-            mLastRot0 = v;
         }
         return v;
     }
@@ -374,4 +395,18 @@
             ((ViewGroup) group.getChildAt(i)).removeAllViews();
         }
     }
+
+    @Override
+    public void onPluginConnected(NavBarButtonProvider plugin) {
+        mPlugins.add(plugin);
+        clearViews();
+        inflateLayout(mCurrentLayout);
+    }
+
+    @Override
+    public void onPluginDisconnected(NavBarButtonProvider plugin) {
+        mPlugins.remove(plugin);
+        clearViews();
+        inflateLayout(mCurrentLayout);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 9e5b881..3a0eb94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -60,6 +60,8 @@
     // slippery nav bar when everything is disabled, e.g. during setup
     final static boolean SLIPPERY_WHEN_DISABLED = true;
 
+    final static boolean ALTERNATE_CAR_MODE_UI = false;
+
     final Display mDisplay;
     View mCurrentView = null;
     View[] mRotatedViews = new View[4];
@@ -95,7 +97,8 @@
     private OnVerticalChangedListener mOnVerticalChangedListener;
     private boolean mLayoutTransitionsEnabled = true;
     private boolean mWakeAndUnlocking;
-    private boolean mCarMode = false;
+    private boolean mUseCarModeUi = false;
+    private boolean mInCarMode = false;
     private boolean mDockedStackExists;
 
     private final SparseArray<ButtonDispatcher> mButtonDisatchers = new SparseArray<>();
@@ -291,7 +294,9 @@
             mMenuIcon = ctx.getDrawable(R.drawable.ic_sysbar_menu);
             mImeIcon = ctx.getDrawable(R.drawable.ic_ime_switcher_default);
 
-            updateCarModeIcons(ctx);
+            if (ALTERNATE_CAR_MODE_UI) {
+                updateCarModeIcons(ctx);
+            }
         }
     }
 
@@ -342,14 +347,14 @@
         // carmode, respectively. Recents are not available in CarMode in nav bar so change
         // to recent icon is not required.
         Drawable backIcon = (backAlt)
-                ? getBackIconWithAlt(mCarMode, mVertical)
-                : getBackIcon(mCarMode, mVertical);
+                ? getBackIconWithAlt(mUseCarModeUi, mVertical)
+                : getBackIcon(mUseCarModeUi, mVertical);
 
         getBackButton().setImageDrawable(backIcon);
 
         updateRecentsIcon();
 
-        if (mCarMode) {
+        if (mUseCarModeUi) {
             getHomeButton().setImageDrawable(mHomeCarModeIcon);
         } else {
             getHomeButton().setImageDrawable(mHomeDefaultIcon);
@@ -377,9 +382,9 @@
 
         final boolean disableHome = ((disabledFlags & View.STATUS_BAR_DISABLE_HOME) != 0);
 
-        // Disable recents always in car mode.
-        boolean disableRecent = (
-                mCarMode || (disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0);
+        // Always disable recents when alternate car mode UI is active.
+        boolean disableRecent = mUseCarModeUi
+                        || ((disabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0);
         final boolean disableBack = ((disabledFlags & View.STATUS_BAR_DISABLE_BACK) != 0)
                 && ((mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) == 0);
         final boolean disableSearch = ((disabledFlags & View.STATUS_BAR_DISABLE_SEARCH) != 0);
@@ -627,14 +632,19 @@
         boolean uiCarModeChanged = false;
         if (newConfig != null) {
             int uiMode = newConfig.uiMode & Configuration.UI_MODE_TYPE_MASK;
-            if (mCarMode && uiMode != Configuration.UI_MODE_TYPE_CAR) {
-                mCarMode = false;
-                uiCarModeChanged = true;
-                getHomeButton().setCarMode(mCarMode);
-            } else if (uiMode == Configuration.UI_MODE_TYPE_CAR) {
-                mCarMode = true;
-                uiCarModeChanged = true;
-                getHomeButton().setCarMode(mCarMode);
+            final boolean isCarMode = (uiMode == Configuration.UI_MODE_TYPE_CAR);
+
+            if (isCarMode != mInCarMode) {
+                mInCarMode = isCarMode;
+                getHomeButton().setCarMode(isCarMode);
+
+                if (ALTERNATE_CAR_MODE_UI) {
+                    mUseCarModeUi = isCarMode;
+                    uiCarModeChanged = true;
+                } else {
+                    // Don't use car mode behavior if ALTERNATE_CAR_MODE_UI not set.
+                    mUseCarModeUi = false;
+                }
             }
         }
         return uiCarModeChanged;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 5d1af2f..fb2c335 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -328,7 +328,7 @@
         } else if (!mQsExpanded) {
             setQsExpansion(mQsMinExpansionHeight + mLastOverscroll);
         }
-        updateStackHeight(getExpandedHeight());
+        updateExpandedHeight(getExpandedHeight());
         updateHeader();
 
         // If we are running a size change animation, the animation takes care of the height of
@@ -376,10 +376,7 @@
         boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
         int stackScrollerPadding;
         if (mStatusBarState != StatusBarState.KEYGUARD) {
-            int bottom = mQsContainer.getHeader().getHeight();
-            stackScrollerPadding = mStatusBarState == StatusBarState.SHADE
-                    ? bottom + mQsPeekHeight
-                    : mKeyguardStatusBar.getHeight();
+            stackScrollerPadding = mQsContainer.getHeader().getHeight() + mQsPeekHeight;
             mTopPaddingAdjustment = 0;
         } else {
             mClockPositionAlgorithm.setup(
@@ -1166,6 +1163,7 @@
 
     private void updateQsState() {
         mQsContainer.setExpanded(mQsExpanded);
+        mNotificationStackScroller.setQsExpanded(mQsExpanded);
         mNotificationStackScroller.setScrollingEnabled(
                 mStatusBarState != StatusBarState.KEYGUARD && (!mQsExpanded
                         || mQsExpansionFromOverscroll));
@@ -1427,7 +1425,7 @@
             setQsExpansion(mQsMinExpansionHeight
                     + t * (getTempQsMaxExpansion() - mQsMinExpansionHeight));
         }
-        updateStackHeight(expandedHeight);
+        updateExpandedHeight(expandedHeight);
         updateHeader();
         updateUnlockIcon();
         updateNotificationTranslucency();
@@ -1487,7 +1485,7 @@
                 maxQsHeight, mStatusBarState == StatusBarState.KEYGUARD
                         ? mClockPositionResult.stackScrollerPadding - mTopPaddingAdjustment
                         : 0)
-                + notificationHeight;
+                + notificationHeight + mNotificationStackScroller.getTopPaddingOverflow();
         if (totalHeight > mNotificationStackScroller.getHeight()) {
             float fullyCollapsedHeight = maxQsHeight
                     + mNotificationStackScroller.getLayoutMinHeight();
@@ -1730,6 +1728,14 @@
         if (view == null && mQsExpanded) {
             return;
         }
+        ExpandableView firstChildNotGone = mNotificationStackScroller.getFirstChildNotGone();
+        ExpandableNotificationRow firstRow = firstChildNotGone instanceof ExpandableNotificationRow
+                ? (ExpandableNotificationRow) firstChildNotGone
+                : null;
+        if (firstRow != null
+                && (view == firstRow || (firstRow.getNotificationParent() == firstRow))) {
+            requestScrollerTopPaddingUpdate(false);
+        }
         requestPanelHeightUpdate();
     }
 
@@ -2249,8 +2255,8 @@
         mQsAutoReinflateContainer.setTranslationX(translation);
     }
 
-    protected void updateStackHeight(float stackHeight) {
-        mNotificationStackScroller.setStackHeight(stackHeight);
+    protected void updateExpandedHeight(float expandedHeight) {
+        mNotificationStackScroller.setExpandedHeight(expandedHeight);
         updateKeyguardBottomAreaAlpha();
     }
 
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 8ee014c..a442fc0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -37,6 +37,7 @@
 import android.app.ActivityManagerNative;
 import android.app.ActivityOptions;
 import android.app.IActivityManager;
+import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
@@ -1765,18 +1766,21 @@
         for (int i=0; i<N; i++) {
             Entry ent = activeNotifications.get(i);
             int vis = ent.notification.getNotification().visibility;
+            int userId = ent.notification.getUserId();
 
             // Display public version of the notification if we need to redact.
-            final boolean hideSensitive =
-                    !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId());
+            boolean deviceSensitive = (isLockscreenPublicMode(mCurrentUserId)
+                    && !userAllowsPrivateNotificationsInPublic(mCurrentUserId));
+            boolean userSensitive = deviceSensitive || (isLockscreenPublicMode(userId)
+                    && !userAllowsPrivateNotificationsInPublic(userId));
             boolean sensitiveNote = vis == Notification.VISIBILITY_PRIVATE;
             boolean sensitivePackage = packageHasVisibilityOverride(ent.notification.getKey());
-            boolean sensitive = (sensitiveNote && hideSensitive) || sensitivePackage;
-            boolean showingPublic = sensitive && isLockscreenPublicMode();
+            boolean sensitive = (sensitiveNote && userSensitive) || sensitivePackage;
+            boolean showingPublic = sensitive && isLockscreenPublicMode(userId);
             if (showingPublic) {
                 updatePublicContentView(ent, ent.notification);
             }
-            ent.row.setSensitive(sensitive, hideSensitive);
+            ent.row.setSensitive(sensitive, deviceSensitive);
             if (ent.autoRedacted && ent.legacy) {
                 // TODO: Also fade this? Or, maybe easier (and better), provide a dark redacted form
                 // for legacy auto redacted notifications.
@@ -4333,17 +4337,23 @@
     }
 
     private void updatePublicMode() {
-        boolean isPublic = false;
-        if (mStatusBarKeyguardViewManager.isShowing()) {
-            for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
-                UserInfo userInfo = mCurrentProfiles.valueAt(i);
-                if (mStatusBarKeyguardViewManager.isSecure(userInfo.id)) {
-                    isPublic = true;
-                    break;
+        final boolean showingKeyguard = mStatusBarKeyguardViewManager.isShowing();
+        final boolean devicePublic = showingKeyguard
+                && mStatusBarKeyguardViewManager.isSecure(mCurrentUserId);
+
+        // Look for public mode users. Users are considered public in either case of:
+        //   - device keyguard is shown in secure mode;
+        //   - profile is locked with a work challenge.
+        for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
+            final int userId = mCurrentProfiles.valueAt(i).id;
+            boolean isProfilePublic = devicePublic;
+            if (!devicePublic && userId != mCurrentUserId) {
+                if (mStatusBarKeyguardViewManager.isSecure(userId)) {
+                    isProfilePublic = mKeyguardManager.isDeviceLocked(userId);
                 }
             }
+            setLockscreenPublicMode(isProfilePublic, userId);
         }
-        setLockscreenPublicMode(isPublic);
     }
 
     protected void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) {
@@ -4400,7 +4410,8 @@
     public void updateStackScrollerState(boolean goingToFullShade, boolean fromShadeLocked) {
         if (mStackScroller == null) return;
         boolean onKeyguard = mState == StatusBarState.KEYGUARD;
-        mStackScroller.setHideSensitive(isLockscreenPublicMode(), goingToFullShade);
+        boolean publicMode = isAnyProfilePublicMode();
+        mStackScroller.setHideSensitive(publicMode, goingToFullShade);
         mStackScroller.setDimmed(onKeyguard, fromShadeLocked /* animate */);
         mStackScroller.setExpandingEnabled(!onKeyguard);
         ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild();
@@ -4650,6 +4661,7 @@
      * @param expandView The view to expand after going to the shade.
      */
     public void goToLockedShade(View expandView) {
+        int userId = mCurrentUserId;
         ExpandableNotificationRow row = null;
         if (expandView instanceof ExpandableNotificationRow) {
             row = (ExpandableNotificationRow) expandView;
@@ -4657,10 +4669,13 @@
             // Indicate that the group expansion is changing at this time -- this way the group
             // and children backgrounds / divider animations will look correct.
             row.setGroupExpansionChanging(true);
+            if (row.getStatusBarNotification() != null) {
+                userId = row.getStatusBarNotification().getUserId();
+            }
         }
         boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId)
                 || !mShowLockscreenNotifications || mFalsingManager.shouldEnforceBouncer();
-        if (isLockscreenPublicMode() && fullShadeNeedsBouncer) {
+        if (isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) {
             mLeaveOpenOnKeyguardHide = true;
             showBouncer();
             mDraggedDownRow = row;
@@ -4705,10 +4720,20 @@
         mPendingWorkRemoteInputView = clicked;
     }
 
+    private boolean isAnyProfilePublicMode() {
+        for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
+            if (isLockscreenPublicMode(mCurrentProfiles.valueAt(i).id)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Override
-    protected void onWorkChallengeUnlocked() {
-        if (mPendingWorkRemoteInputView != null) {
-            final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView;
+    protected void onWorkChallengeChanged() {
+        updatePublicMode();
+        updateNotifications();
+        if (mPendingWorkRemoteInputView != null && !isAnyProfilePublicMode()) {
             // Expand notification panel and the notification row, then click on remote input view
             final Runnable clickPendingViewRunnable = new Runnable() {
                 @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index b9c7a4b..6726c92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -53,6 +53,7 @@
     protected boolean mCharged;
     protected boolean mPowerSave;
     private boolean mTestmode = false;
+    private boolean mHasReceivedBattery = false;
 
     public BatteryControllerImpl(Context context) {
         mContext = context;
@@ -92,6 +93,7 @@
         synchronized (mChangeCallbacks) {
             mChangeCallbacks.add(cb);
         }
+        if (!mHasReceivedBattery) return;
         cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
         cb.onPowerSaveChanged(mPowerSave);
     }
@@ -108,6 +110,7 @@
         final String action = intent.getAction();
         if (action.equals(Intent.ACTION_BATTERY_CHANGED)) {
             if (mTestmode && !intent.getBooleanExtra("testmode", false)) return;
+            mHasReceivedBattery = true;
             mLevel = (int)(100f
                     * intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
                     / intent.getIntExtra(BatteryManager.EXTRA_SCALE, 100));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index e6066aa..bcc5a3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -44,12 +44,12 @@
 import android.widget.ImageView;
 
 import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.ButtonDispatcher;
+import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
 
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
 
-public class KeyButtonView extends ImageView implements ButtonDispatcher.ButtonInterface {
+public class KeyButtonView extends ImageView implements ButtonInterface {
 
     private int mContentDescriptionRes;
     private long mDownTime;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 50e5b88..81da672 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -43,6 +43,7 @@
     private boolean mShadeExpanded;
     private float mMaxHeadsUpTranslation;
     private boolean mDismissAllInProgress;
+    private int mLayoutMinHeight;
 
     public int getScrollY() {
         return mScrollY;
@@ -137,10 +138,6 @@
         mStackTranslation = stackTranslation;
     }
 
-    public int getLayoutHeight() {
-        return mLayoutHeight;
-    }
-
     public void setLayoutHeight(int layoutHeight) {
         mLayoutHeight = layoutHeight;
     }
@@ -154,7 +151,7 @@
     }
 
     public int getInnerHeight() {
-        return mLayoutHeight - mTopPadding;
+        return Math.max(mLayoutHeight - mTopPadding, mLayoutMinHeight);
     }
 
     public boolean isShadeExpanded() {
@@ -180,4 +177,8 @@
     public boolean isDismissAllInProgress() {
         return mDismissAllInProgress;
     }
+
+    public void setLayoutMinHeight(int layoutMinHeight) {
+        mLayoutMinHeight = layoutMinHeight;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 90f4100..9dc9062 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -112,11 +112,7 @@
     private int mCurrentStackHeight = Integer.MAX_VALUE;
     private final Paint mBackgroundPaint = new Paint();
 
-    /**
-     * mCurrentStackHeight is the actual stack height, mLastSetStackHeight is the stack height set
-     * externally from {@link #setStackHeight}
-     */
-    private float mLastSetStackHeight;
+    private float mExpandedHeight;
     private int mOwnScrollY;
     private int mMaxLayoutHeight;
 
@@ -355,6 +351,9 @@
                     return object.getBackgroundFadeAmount();
                 }
             };
+    private boolean mQsExpanded;
+    private boolean mForwardScrollable;
+    private boolean mBackwardScrollable;
 
     public NotificationStackScrollLayout(Context context) {
         this(context, null);
@@ -520,6 +519,7 @@
         clampScrollPosition();
         requestChildrenUpdate();
         updateFirstAndLastBackgroundViews();
+        updateAlgorithmLayoutMinHeight();
     }
 
     private void requestAnimationOnViewResize(ExpandableNotificationRow row) {
@@ -561,9 +561,14 @@
 
     private void updateAlgorithmHeightAndPadding() {
         mAmbientState.setLayoutHeight(getLayoutHeight());
+        updateAlgorithmLayoutMinHeight();
         mAmbientState.setTopPadding(mTopPadding);
     }
 
+    private void updateAlgorithmLayoutMinHeight() {
+        mAmbientState.setLayoutMinHeight(mQsExpanded && !onKeyguard() ? getLayoutMinHeight() : 0);
+    }
+
     /**
      * Updates the children views according to the stack scroll algorithm. Call this whenever
      * modifications to {@link #mOwnScrollY} are performed to reflect it in the view layout.
@@ -594,7 +599,7 @@
                 if (startingPosition < mOwnScrollY) {
                     // This child starts off screen, so let's keep it offscreen to keep the others visible
 
-                    mOwnScrollY += childHeight;
+                    setOwnScrollY(mOwnScrollY + childHeight);
                 }
             }
         }
@@ -617,7 +622,7 @@
             // Only apply the scroll if we're scrolling the view upwards, or the view is so far up
             // that it is not visible anymore.
             if (mOwnScrollY < targetScroll || outOfViewScroll < mOwnScrollY) {
-                mOwnScrollY = targetScroll;
+                setOwnScrollY(targetScroll);
             }
         }
     }
@@ -637,7 +642,7 @@
     private void clampScrollPosition() {
         int scrollRange = getScrollRange();
         if (scrollRange < mOwnScrollY) {
-            mOwnScrollY = scrollRange;
+            setOwnScrollY(scrollRange);
         }
     }
 
@@ -660,19 +665,19 @@
     }
 
     /**
-     * Update the height of the stack to a new height.
+     * Update the height of the panel.
      *
-     * @param height the new height of the stack
+     * @param height the expanded height of the panel
      */
-    public void setStackHeight(float height) {
-        mLastSetStackHeight = height;
+    public void setExpandedHeight(float height) {
+        mExpandedHeight = height;
         setIsExpanded(height > 0.0f);
         int stackHeight;
         float translationY;
         float appearEndPosition = getAppearEndPosition();
         float appearStartPosition = getAppearStartPosition();
         if (height >= appearEndPosition) {
-            translationY = mTopPaddingOverflow;
+            translationY = 0;
             stackHeight = (int) height;
         } else {
             float appearFraction = getAppearFraction(height);
@@ -699,8 +704,12 @@
      *         Measured relative to the resting position.
      */
     private float getExpandTranslationStart() {
-        int startPosition = mTrackingHeadsUp || mHeadsUpManager.hasPinnedHeadsUp()
-                ? 0 : -getFirstChildIntrinsicHeight();
+        int startPosition = 0;
+        if (!mTrackingHeadsUp && !mHeadsUpManager.hasPinnedHeadsUp()) {
+            startPosition = - Math.min(getFirstChildIntrinsicHeight(),
+                    mMaxLayoutHeight - mIntrinsicPadding - mBottomStackSlowDownHeight
+                            - mBottomStackPeekSize);
+        }
         return startPosition - mTopPadding;
     }
 
@@ -723,7 +732,7 @@
                 ? mHeadsUpManager.getTopHeadsUpPinnedHeight() + mBottomStackPeekSize
                         + mBottomStackSlowDownHeight
                 : getLayoutMinHeight();
-        return firstItemHeight + mTopPadding + mTopPaddingOverflow;
+        return firstItemHeight + (onKeyguard() ? mTopPadding : mIntrinsicPadding);
     }
 
     /**
@@ -1153,6 +1162,10 @@
 
     @Override
     public boolean isAntiFalsingNeeded() {
+        return onKeyguard();
+    }
+
+    private boolean onKeyguard() {
         return mPhoneStatusBar.getBarState() == StatusBarState.KEYGUARD;
     }
 
@@ -1262,7 +1275,7 @@
         if (!isScrollingEnabled()) {
             return false;
         }
-        if (isInsideQsContainer(ev)) {
+        if (isInsideQsContainer(ev) && !mIsBeingDragged) {
             return false;
         }
         mForcedScroll = null;
@@ -1431,7 +1444,7 @@
                         false /* onTop */,
                         false /* animate */);
             }
-            mOwnScrollY = range;
+            setOwnScrollY(range);
             scrollAmount = 0.0f;
         }
         return scrollAmount;
@@ -1462,7 +1475,7 @@
             setOverScrolledPixels(currentTopPixels - newScrollY,
                     true /* onTop */,
                     false /* animate */);
-            mOwnScrollY = 0;
+            setOwnScrollY(0);
             scrollAmount = 0.0f;
         }
         return scrollAmount;
@@ -1676,7 +1689,7 @@
     }
 
     private void customScrollTo(int y) {
-        mOwnScrollY = y;
+        setOwnScrollY(y);
         updateChildren();
     }
 
@@ -1687,7 +1700,7 @@
             final int oldX = mScrollX;
             final int oldY = mOwnScrollY;
             mScrollX = scrollX;
-            mOwnScrollY = scrollY;
+            setOwnScrollY(scrollY);
             if (clampedY) {
                 springBack();
             } else {
@@ -1717,12 +1730,12 @@
             if (overScrolledTop) {
                 onTop = true;
                 newAmount = -mOwnScrollY;
-                mOwnScrollY = 0;
+                setOwnScrollY(0);
                 mDontReportNextOverScroll = true;
             } else {
                 onTop = false;
                 newAmount = mOwnScrollY - scrollRange;
-                mOwnScrollY = scrollRange;
+                setOwnScrollY(scrollRange);
             }
             setOverScrollAmount(newAmount, onTop, false);
             setOverScrollAmount(0.0f, onTop, true);
@@ -1850,6 +1863,19 @@
         if (scrollable != mScrollable) {
             mScrollable = scrollable;
             setFocusable(scrollable);
+            updateForwardAndBackwardScrollability();
+        }
+    }
+
+    private void updateForwardAndBackwardScrollability() {
+        boolean forwardScrollable = mScrollable && mOwnScrollY < getScrollRange();
+        boolean backwardsScrollable = mScrollable && mOwnScrollY > 0;
+        boolean changed = forwardScrollable != mForwardScrollable
+                || backwardsScrollable != mBackwardScrollable;
+        mForwardScrollable = forwardScrollable;
+        mBackwardScrollable = backwardsScrollable;
+        if (changed) {
+            sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
         }
     }
 
@@ -2109,13 +2135,13 @@
             float topAmount = getCurrentOverScrollAmount(true);
             float bottomAmount = getCurrentOverScrollAmount(false);
             if (velocityY < 0 && topAmount > 0) {
-                mOwnScrollY -= (int) topAmount;
+                setOwnScrollY(mOwnScrollY - (int) topAmount);
                 mDontReportNextOverScroll = true;
                 setOverScrollAmount(0, true, false);
                 mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
                         * mOverflingDistance + topAmount;
             } else if (velocityY > 0 && bottomAmount > 0) {
-                mOwnScrollY += bottomAmount;
+                setOwnScrollY((int) (mOwnScrollY + bottomAmount));
                 setOverScrollAmount(0, false, false);
                 mMaxOverScroll = Math.abs(velocityY) / 1000f
                         * getRubberBandFactor(false /* onTop */) * mOverflingDistance
@@ -2158,26 +2184,22 @@
      */
     public void updateTopPadding(float qsHeight, boolean animate,
             boolean ignoreIntrinsicPadding) {
-        float start = qsHeight;
-        float stackHeight = getHeight() - start;
+        int topPadding = (int) qsHeight;
         int minStackHeight = getLayoutMinHeight();
-        if (stackHeight <= minStackHeight) {
-            float overflow = minStackHeight - stackHeight;
-            stackHeight = minStackHeight;
-            start = getHeight() - stackHeight;
-            mTopPaddingOverflow = overflow;
+        if (topPadding + minStackHeight > getHeight()) {
+            mTopPaddingOverflow = topPadding + minStackHeight - getHeight();
         } else {
             mTopPaddingOverflow = 0;
         }
-        setTopPadding(ignoreIntrinsicPadding ? (int) start : clampPadding((int) start),
+        setTopPadding(ignoreIntrinsicPadding ? topPadding : clampPadding(topPadding),
                 animate);
-        setStackHeight(mLastSetStackHeight);
+        setExpandedHeight(mExpandedHeight);
     }
 
     public int getLayoutMinHeight() {
         int firstChildMinHeight = getFirstChildIntrinsicHeight();
         return Math.min(firstChildMinHeight + mBottomStackPeekSize + mBottomStackSlowDownHeight,
-                mMaxLayoutHeight - mTopPadding);
+                mMaxLayoutHeight - mIntrinsicPadding);
     }
 
     public int getFirstChildIntrinsicHeight() {
@@ -2470,11 +2492,11 @@
         if (endPosition <= mOwnScrollY) {
             // This child is fully scrolled of the top, so we have to deduct its height from the
             // scrollPosition
-            mOwnScrollY -= childHeight;
+            setOwnScrollY(mOwnScrollY - childHeight);
         } else if (startingPosition < mOwnScrollY) {
             // This child is currently being scrolled into, set the scroll position to the start of
             // this child
-            mOwnScrollY = startingPosition;
+            setOwnScrollY(startingPosition);
         }
     }
 
@@ -3059,7 +3081,7 @@
     public void onExpansionStopped() {
         mIsExpansionChanging = false;
         if (!mIsExpanded) {
-            mOwnScrollY = 0;
+            setOwnScrollY(0);
             mPhoneStatusBar.resetUserExpandedStates();
 
             // lets make sure nothing is in the overlay / transient anymore
@@ -3092,7 +3114,7 @@
 
     public void resetScrollPosition() {
         mScroller.abortAnimation();
-        mOwnScrollY = 0;
+        setOwnScrollY(0);
     }
 
     private void setIsExpanded(boolean isExpanded) {
@@ -3128,10 +3150,14 @@
         updateScrollPositionOnExpandInBottom(view);
         clampScrollPosition();
         notifyHeightChangeListener(view);
+        ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
+                ? (ExpandableNotificationRow) view
+                : null;
+        if (row != null && (row == mFirstVisibleBackgroundChild
+                || row.getNotificationParent() == mFirstVisibleBackgroundChild)) {
+            updateAlgorithmLayoutMinHeight();
+        }
         if (needsAnimation) {
-            ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
-                    ? (ExpandableNotificationRow) view
-                    : null;
             requestAnimationOnViewResize(row);
         }
         requestChildrenUpdate();
@@ -3157,7 +3183,7 @@
                 }
                 int stackEnd = getStackEndPosition();
                 if (endPosition > stackEnd) {
-                    mOwnScrollY += endPosition - stackEnd;
+                    setOwnScrollY((int) (mOwnScrollY + endPosition - stackEnd));
                     mDisallowScrollingInThisMotion = true;
                 }
             }
@@ -3414,7 +3440,7 @@
     }
 
     private int findDarkAnimationOriginIndex(@Nullable PointF screenLocation) {
-        if (screenLocation == null || screenLocation.y < mTopPadding + mTopPaddingOverflow) {
+        if (screenLocation == null || screenLocation.y < mTopPadding) {
             return AnimationEvent.DARK_ANIMATION_ORIGIN_INDEX_ABOVE;
         }
         if (screenLocation.y > getBottomMostNotificationBottom()) {
@@ -3719,15 +3745,14 @@
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        final int scrollRange = getScrollRange();
-        if (scrollRange > 0) {
+        if (mScrollable) {
             info.setScrollable(true);
-            if (mScrollY > 0) {
+            if (mBackwardScrollable) {
                 info.addAction(
                         AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD);
                 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP);
             }
-            if (mScrollY < scrollRange) {
+            if (mForwardScrollable) {
                 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD);
                 info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_DOWN);
             }
@@ -3898,6 +3923,18 @@
         mCurrentStackScrollState.removeViewStateForView(view);
     }
 
+    public void setQsExpanded(boolean qsExpanded) {
+        mQsExpanded = qsExpanded;
+        updateAlgorithmLayoutMinHeight();
+    }
+
+    public void setOwnScrollY(int ownScrollY) {
+        if (ownScrollY != mOwnScrollY) {
+            mOwnScrollY = ownScrollY;
+            updateForwardAndBackwardScrollability();
+        }
+    }
+
     /**
      * A listener that is notified when some child locations might have changed.
      */
@@ -4121,7 +4158,7 @@
             onDragCancelled(animView);
 
             // If we're on the lockscreen we want to false this.
-            if (mPhoneStatusBar.getBarState() == StatusBarState.KEYGUARD) {
+            if (isAntiFalsingNeeded()) {
                 mHandler.removeCallbacks(mFalsingCheck);
                 mHandler.postDelayed(mFalsingCheck, COVER_GEAR_DELAY);
             }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java b/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java
new file mode 100644
index 0000000..5cb5e68
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/BatteryMeterDrawableTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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.systemui;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyFloat;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BatteryMeterDrawableTest {
+
+    private Context mContext;
+    private Resources mResources;
+    private BatteryMeterDrawable mBatteryMeter;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mResources = mContext.getResources();
+        mBatteryMeter = new BatteryMeterDrawable(mContext, 0);
+    }
+
+    @Test
+    public void testGetIntrinsicSize() {
+        assertEquals(
+                mResources.getDimensionPixelSize(R.dimen.battery_width),
+                mBatteryMeter.getIntrinsicWidth());
+        assertEquals(
+                mResources.getDimensionPixelSize(R.dimen.battery_height),
+                mBatteryMeter.getIntrinsicHeight());
+    }
+
+    @Test
+    public void testDrawNothingBeforeOnBatteryLevelChanged() {
+        final Canvas canvas = mock(Canvas.class);
+        mBatteryMeter.draw(canvas);
+        verify(canvas, never()).drawPath(any(), any());
+        verify(canvas, never()).drawText(anyString(), anyFloat(), anyFloat(), any());
+    }
+
+    @Test
+    public void testDrawImageButNoTextIfPluggedIn() {
+        mBatteryMeter.onBatteryLevelChanged(0, true, true);
+        final Canvas canvas = mock(Canvas.class);
+        mBatteryMeter.draw(canvas);
+        verify(canvas, atLeastOnce()).drawPath(any(), any());
+        verify(canvas, never()).drawText(anyString(), anyFloat(), anyFloat(), any());
+    }
+
+    @Test
+    public void testDrawTextIfNotPluggedIn() {
+        mBatteryMeter.onBatteryLevelChanged(0, false, false);
+        final Canvas canvas = mock(Canvas.class);
+        mBatteryMeter.draw(canvas);
+        verify(canvas, times(1)).drawText(anyString(), anyFloat(), anyFloat(), any());
+    }
+
+    @Test
+    public void testDrawNoTextIfPowerSaveEnabled() {
+        mBatteryMeter.onBatteryLevelChanged(0, false, false);
+        mBatteryMeter.onPowerSaveChanged(true);
+        final Canvas canvas = mock(Canvas.class);
+        mBatteryMeter.draw(canvas);
+        verify(canvas, never()).drawText(anyString(), anyFloat(), anyFloat(), any());
+    }
+
+    @Test
+    public void testDrawTextWarningAtCriticalLevel() {
+        int criticalLevel = mResources.getInteger(
+                com.android.internal.R.integer.config_criticalBatteryWarningLevel);
+        mBatteryMeter.onBatteryLevelChanged(criticalLevel, false, false);
+        final Canvas canvas = mock(Canvas.class);
+        mBatteryMeter.draw(canvas);
+        String warningString = mResources.getString(R.string.battery_meter_very_low_overlay_symbol);
+        verify(canvas, times(1)).drawText(eq(warningString), anyFloat(), anyFloat(), any());
+    }
+
+    @Test
+    public void testDrawTextNoWarningAboveCriticalLevel() {
+        int criticalLevel = mResources.getInteger(
+                com.android.internal.R.integer.config_criticalBatteryWarningLevel);
+        mBatteryMeter.onBatteryLevelChanged(criticalLevel + 1, false, false);
+        final Canvas canvas = mock(Canvas.class);
+        mBatteryMeter.draw(canvas);
+        String warningString = mResources.getString(R.string.battery_meter_very_low_overlay_symbol);
+        verify(canvas, never()).drawText(eq(warningString), anyFloat(), anyFloat(), any());
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
index ab7de39..9050b83 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java
@@ -33,13 +33,11 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.net.Uri;
 import android.os.HandlerThread;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.PluginInstanceManager.ClassLoaderFactory;
 
 import org.junit.After;
 import org.junit.Before;
@@ -64,6 +62,7 @@
     private PackageManager mMockPm;
     private PluginListener mMockListener;
     private PluginInstanceManager mPluginInstanceManager;
+    private PluginManager mMockManager;
 
     @Before
     public void setup() throws Exception {
@@ -72,9 +71,11 @@
         mContextWrapper = new MyContextWrapper(getContext());
         mMockPm = mock(PackageManager.class);
         mMockListener = mock(PluginListener.class);
+        mMockManager = mock(PluginManager.class);
+        when(mMockManager.getClassLoader(Mockito.any(), Mockito.any()))
+                .thenReturn(getClass().getClassLoader());
         mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
-                mMockListener, true, mHandlerThread.getLooper(), 1, true,
-                new TestClassLoaderFactory());
+                mMockListener, true, mHandlerThread.getLooper(), 1, mMockManager, true);
         sMockPlugin = mock(Plugin.class);
         when(sMockPlugin.getVersion()).thenReturn(1);
     }
@@ -89,7 +90,7 @@
     public void testNoPlugins() {
         when(mMockPm.queryIntentServices(Mockito.any(), Mockito.anyInt())).thenReturn(
                 Collections.emptyList());
-        mPluginInstanceManager.startListening();
+        mPluginInstanceManager.loadAll();
 
         waitForIdleSync(mPluginInstanceManager.mPluginHandler);
         waitForIdleSync(mPluginInstanceManager.mMainHandler);
@@ -112,7 +113,7 @@
     public void testPluginDestroy() {
         createPlugin(); // Get into valid created state.
 
-        mPluginInstanceManager.stopListening();
+        mPluginInstanceManager.destroy();
 
         waitForIdleSync(mPluginInstanceManager.mPluginHandler);
         waitForIdleSync(mPluginInstanceManager.mMainHandler);
@@ -127,7 +128,7 @@
         setupFakePmQuery();
         when(sMockPlugin.getVersion()).thenReturn(2);
 
-        mPluginInstanceManager.startListening();
+        mPluginInstanceManager.loadAll();
 
         waitForIdleSync(mPluginInstanceManager.mPluginHandler);
         waitForIdleSync(mPluginInstanceManager.mMainHandler);
@@ -141,10 +142,7 @@
     public void testReloadOnChange() {
         createPlugin(); // Get into valid created state.
 
-        // Send a package changed broadcast.
-        Intent i = new Intent(Intent.ACTION_PACKAGE_CHANGED,
-                Uri.fromParts("package", "com.android.systemui", null));
-        mPluginInstanceManager.onReceive(mContextWrapper, i);
+        mPluginInstanceManager.onPackageChange("com.android.systemui");
 
         waitForIdleSync(mPluginInstanceManager.mPluginHandler);
         waitForIdleSync(mPluginInstanceManager.mMainHandler);
@@ -164,11 +162,10 @@
     public void testNonDebuggable() {
         // Create a version that thinks the build is not debuggable.
         mPluginInstanceManager = new PluginInstanceManager(mContextWrapper, mMockPm, "myAction",
-                mMockListener, true, mHandlerThread.getLooper(), 1, false,
-                new TestClassLoaderFactory());
+                mMockListener, true, mHandlerThread.getLooper(), 1, mMockManager, false);
         setupFakePmQuery();
 
-        mPluginInstanceManager.startListening();
+        mPluginInstanceManager.loadAll();
 
         waitForIdleSync(mPluginInstanceManager.mPluginHandler);
         waitForIdleSync(mPluginInstanceManager.mMainHandler);;
@@ -236,19 +233,12 @@
     private void createPlugin() {
         setupFakePmQuery();
 
-        mPluginInstanceManager.startListening();
+        mPluginInstanceManager.loadAll();
 
         waitForIdleSync(mPluginInstanceManager.mPluginHandler);
         waitForIdleSync(mPluginInstanceManager.mMainHandler);
     }
 
-    private static class TestClassLoaderFactory extends ClassLoaderFactory {
-        @Override
-        public ClassLoader createClassLoader(String path, ClassLoader base) {
-            return base;
-        }
-    }
-
     // Real context with no registering/unregistering of receivers.
     private static class MyContextWrapper extends ContextWrapper {
         public MyContextWrapper(Context base) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
index 56e742a..4b1827d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
@@ -51,7 +51,7 @@
         mMockFactory = mock(PluginInstanceManagerFactory.class);
         mMockPluginInstance = mock(PluginInstanceManager.class);
         when(mMockFactory.createPluginInstanceManager(Mockito.any(), Mockito.any(), Mockito.any(),
-                Mockito.anyBoolean(), Mockito.any(), Mockito.anyInt()))
+                Mockito.anyBoolean(), Mockito.any(), Mockito.anyInt(), Mockito.any()))
                 .thenReturn(mMockPluginInstance);
         mPluginManager = new PluginManager(getContext(), mMockFactory, true, mMockExceptionHandler);
         resetExceptionHandler();
@@ -62,7 +62,7 @@
     public void testAddListener() {
         mPluginManager.addPluginListener("myAction", mMockListener, 1);
 
-        verify(mMockPluginInstance).startListening();
+        verify(mMockPluginInstance).loadAll();
     }
 
     @Test
@@ -70,7 +70,7 @@
         mPluginManager.addPluginListener("myAction", mMockListener, 1);
 
         mPluginManager.removePluginListener(mMockListener);
-        verify(mMockPluginInstance).stopListening();
+        verify(mMockPluginInstance).destroy();
     }
 
     @Test
@@ -80,7 +80,7 @@
         resetExceptionHandler();
         mPluginManager.addPluginListener("myAction", mMockListener, 1);
 
-        verify(mMockPluginInstance, Mockito.never()).startListening();
+        verify(mMockPluginInstance, Mockito.never()).loadAll();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java
new file mode 100644
index 0000000..2792d8c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java
@@ -0,0 +1,85 @@
+package com.android.systemui.statusbar;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertNull;
+
+import static org.mockito.Mockito.mock;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.internal.statusbar.StatusBarIcon;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.phone.StatusBarIconList;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class StatusBarIconListTest extends SysuiTestCase {
+
+    private final static String[] STATUS_BAR_SLOTS = {"aaa", "bbb", "ccc"};
+
+    @Test
+    public void testGetExistingSlot() {
+        StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
+        assertEquals(1, statusBarIconList.getSlotIndex("bbb"));
+        assertEquals(2, statusBarIconList.getSlotIndex("ccc"));
+    }
+
+    @Test
+    public void testGetNonexistingSlot() {
+        StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
+        assertEquals(0, statusBarIconList.getSlotIndex("aaa"));
+        assertEquals(3, statusBarIconList.size());
+        assertEquals(0, statusBarIconList.getSlotIndex("zzz")); // new content added in front
+        assertEquals(1, statusBarIconList.getSlotIndex("aaa")); // slid back
+        assertEquals(4, statusBarIconList.size());
+    }
+
+    @Test
+    public void testAddSlotSlidesIcons() {
+        StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
+        StatusBarIcon sbIcon = mock(StatusBarIcon.class);
+        statusBarIconList.setIcon(0, sbIcon);
+        statusBarIconList.getSlotIndex("zzz"); // new content added in front
+        assertNull(statusBarIconList.getIcon(0));
+        assertEquals(sbIcon, statusBarIconList.getIcon(1));
+    }
+
+    @Test
+    public void testGetAndSetIcon() {
+        StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
+        StatusBarIcon sbIconA = mock(StatusBarIcon.class);
+        StatusBarIcon sbIconB = mock(StatusBarIcon.class);
+        statusBarIconList.setIcon(0, sbIconA);
+        statusBarIconList.setIcon(1, sbIconB);
+        assertEquals(sbIconA, statusBarIconList.getIcon(0));
+        assertEquals(sbIconB, statusBarIconList.getIcon(1));
+        assertNull(statusBarIconList.getIcon(2)); // icon not set
+    }
+
+    @Test
+    public void testRemoveIcon() {
+        StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
+        StatusBarIcon sbIconA = mock(StatusBarIcon.class);
+        StatusBarIcon sbIconB = mock(StatusBarIcon.class);
+        statusBarIconList.setIcon(0, sbIconA);
+        statusBarIconList.setIcon(1, sbIconB);
+        statusBarIconList.removeIcon(0);
+        assertNull(statusBarIconList.getIcon(0)); // icon not set
+    }
+
+    @Test
+    public void testGetViewIndex() {
+        StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS);
+        StatusBarIcon sbIcon = mock(StatusBarIcon.class);
+        statusBarIconList.setIcon(2, sbIcon);
+        assertEquals(0, statusBarIconList.getViewIndex(2)); // Icon for item 2 is 0th child view.
+        statusBarIconList.setIcon(0, sbIcon);
+        assertEquals(0, statusBarIconList.getViewIndex(0)); // Icon for item 0 is 0th child view,
+        assertEquals(1, statusBarIconList.getViewIndex(2)); // and item 2 is now 1st child view.
+    }
+
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 238bf92..7c2eea3f 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -51,6 +51,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
+import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.os.UserHandle;
 import android.os.storage.MountServiceInternal;
@@ -1787,8 +1788,9 @@
     }
 
     @Override public void onShellCommand(FileDescriptor in, FileDescriptor out,
-            FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
-        (new Shell(this, this)).exec(this, in, out, err, args, resultReceiver);
+            FileDescriptor err, String[] args, ShellCallback callback,
+            ResultReceiver resultReceiver) {
+        (new Shell(this, this)).exec(this, in, out, err, args, callback, resultReceiver);
     }
 
     static void dumpCommandHelp(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 6b51721..d2cfb6d 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -20,6 +20,7 @@
 import android.os.BatteryStats;
 
 import android.os.ResultReceiver;
+import android.os.ShellCallback;
 import android.os.ShellCommand;
 import com.android.internal.app.IBatteryStats;
 import com.android.server.am.BatteryStatsService;
@@ -789,7 +790,7 @@
                 pw.println("  technology: " + mBatteryProps.batteryTechnology);
             } else {
                 Shell shell = new Shell();
-                shell.exec(mBinderService, null, fd, null, args, new ResultReceiver(null));
+                shell.exec(mBinderService, null, fd, null, args, null, new ResultReceiver(null));
             }
         }
     }
@@ -875,8 +876,9 @@
         }
 
         @Override public void onShellCommand(FileDescriptor in, FileDescriptor out,
-                FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
-            (new Shell()).exec(this, in, out, err, args, resultReceiver);
+                FileDescriptor err, String[] args, ShellCallback callback,
+                ResultReceiver resultReceiver) {
+            (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
         }
     }
 
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index 6b73fec..dbc1f31 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -60,6 +60,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
+import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -1232,8 +1233,8 @@
         }
 
         @Override public void onShellCommand(FileDescriptor in, FileDescriptor out,
-                FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
-            (new Shell()).exec(this, in, out, err, args, resultReceiver);
+                FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
+            (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
         }
     }
 
@@ -2838,7 +2839,8 @@
                     shell.userId = userId;
                     String[] newArgs = new String[args.length-i];
                     System.arraycopy(args, i, newArgs, 0, args.length-i);
-                    shell.exec(mBinderService, null, fd, null, newArgs, new ResultReceiver(null));
+                    shell.exec(mBinderService, null, fd, null, newArgs, null,
+                            new ResultReceiver(null));
                     return;
                 }
             }
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 8f16504..e64aa16 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -77,6 +77,7 @@
 import android.os.StrictMode;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.os.Trace;
 import android.provider.Settings;
 import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.PhoneStateListener;
@@ -1699,6 +1700,7 @@
                 return;
             }
 
+            Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "inetd bandwidth");
             try {
                 mConnector.execute("bandwidth", suffix + chain, uid);
                 if (enable) {
@@ -1708,6 +1710,8 @@
                 }
             } catch (NativeDaemonConnectorException e) {
                 throw e.rethrowAsParcelableException();
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
             }
         }
     }
@@ -1730,6 +1734,7 @@
                 Log.w(TAG, "setDataSaverMode(): already " + mDataSaverMode);
                 return true;
             }
+            Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "bandwidthEnableDataSaver");
             try {
                 final boolean changed = mNetdService.bandwidthEnableDataSaver(enable);
                 if (changed) {
@@ -1741,6 +1746,8 @@
             } catch (RemoteException e) {
                 Log.w(TAG, "setDataSaverMode(" + enable + "): netd command failed", e);
                 return false;
+            } finally {
+                Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
             }
         }
     }
diff --git a/services/core/java/com/android/server/RecoverySystemService.java b/services/core/java/com/android/server/RecoverySystemService.java
index 276687f..3c8c699 100644
--- a/services/core/java/com/android/server/RecoverySystemService.java
+++ b/services/core/java/com/android/server/RecoverySystemService.java
@@ -21,6 +21,7 @@
 import android.net.LocalSocketAddress;
 import android.os.IRecoverySystem;
 import android.os.IRecoverySystemProgressListener;
+import android.os.PowerManager;
 import android.os.RecoverySystem;
 import android.os.RemoteException;
 import android.os.SystemProperties;
@@ -50,8 +51,15 @@
     // The socket at /dev/socket/uncrypt to communicate with uncrypt.
     private static final String UNCRYPT_SOCKET = "uncrypt";
 
+    // The init services that communicate with /system/bin/uncrypt.
+    private static final String INIT_SERVICE_UNCRYPT = "init.svc.uncrypt";
+    private static final String INIT_SERVICE_SETUP_BCB = "init.svc.setup-bcb";
+    private static final String INIT_SERVICE_CLEAR_BCB = "init.svc.clear-bcb";
+
     private static final int SOCKET_CONNECTION_MAX_RETRY = 30;
 
+    private static final Object sRequestLock = new Object();
+
     private Context mContext;
 
     public RecoverySystemService(Context context) {
@@ -69,95 +77,155 @@
         public boolean uncrypt(String filename, IRecoverySystemProgressListener listener) {
             if (DEBUG) Slog.d(TAG, "uncrypt: " + filename);
 
-            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
+            synchronized (sRequestLock) {
+                mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
 
-            // Write the filename into UNCRYPT_PACKAGE_FILE to be read by
-            // uncrypt.
-            RecoverySystem.UNCRYPT_PACKAGE_FILE.delete();
-
-            try (FileWriter uncryptFile = new FileWriter(RecoverySystem.UNCRYPT_PACKAGE_FILE)) {
-                uncryptFile.write(filename + "\n");
-            } catch (IOException e) {
-                Slog.e(TAG, "IOException when writing \"" + RecoverySystem.UNCRYPT_PACKAGE_FILE +
-                        "\": ", e);
-                return false;
-            }
-
-            // Trigger uncrypt via init.
-            SystemProperties.set("ctl.start", "uncrypt");
-
-            // Connect to the uncrypt service socket.
-            LocalSocket socket = connectService();
-            if (socket == null) {
-                Slog.e(TAG, "Failed to connect to uncrypt socket");
-                return false;
-            }
-
-            // Read the status from the socket.
-            DataInputStream dis = null;
-            DataOutputStream dos = null;
-            try {
-                dis = new DataInputStream(socket.getInputStream());
-                dos = new DataOutputStream(socket.getOutputStream());
-                int lastStatus = Integer.MIN_VALUE;
-                while (true) {
-                    int status = dis.readInt();
-                    // Avoid flooding the log with the same message.
-                    if (status == lastStatus && lastStatus != Integer.MIN_VALUE) {
-                        continue;
-                    }
-                    lastStatus = status;
-
-                    if (status >= 0 && status <= 100) {
-                        // Update status
-                        Slog.i(TAG, "uncrypt read status: " + status);
-                        if (listener != null) {
-                            try {
-                                listener.onProgress(status);
-                            } catch (RemoteException ignored) {
-                                Slog.w(TAG, "RemoteException when posting progress");
-                            }
-                        }
-                        if (status == 100) {
-                            Slog.i(TAG, "uncrypt successfully finished.");
-                            // Ack receipt of the final status code. uncrypt
-                            // waits for the ack so the socket won't be
-                            // destroyed before we receive the code.
-                            dos.writeInt(0);
-                            break;
-                        }
-                    } else {
-                        // Error in /system/bin/uncrypt.
-                        Slog.e(TAG, "uncrypt failed with status: " + status);
-                        // Ack receipt of the final status code. uncrypt waits
-                        // for the ack so the socket won't be destroyed before
-                        // we receive the code.
-                        dos.writeInt(0);
-                        return false;
-                    }
+                final boolean available = checkAndWaitForUncryptService();
+                if (!available) {
+                    Slog.e(TAG, "uncrypt service is unavailable.");
+                    return false;
                 }
-            } catch (IOException e) {
-                Slog.e(TAG, "IOException when reading status: ", e);
-                return false;
-            } finally {
-                IoUtils.closeQuietly(dis);
-                IoUtils.closeQuietly(dos);
-                IoUtils.closeQuietly(socket);
-            }
 
-            return true;
+                // Write the filename into UNCRYPT_PACKAGE_FILE to be read by
+                // uncrypt.
+                RecoverySystem.UNCRYPT_PACKAGE_FILE.delete();
+
+                try (FileWriter uncryptFile = new FileWriter(RecoverySystem.UNCRYPT_PACKAGE_FILE)) {
+                    uncryptFile.write(filename + "\n");
+                } catch (IOException e) {
+                    Slog.e(TAG, "IOException when writing \"" +
+                            RecoverySystem.UNCRYPT_PACKAGE_FILE + "\":", e);
+                    return false;
+                }
+
+                // Trigger uncrypt via init.
+                SystemProperties.set("ctl.start", "uncrypt");
+
+                // Connect to the uncrypt service socket.
+                LocalSocket socket = connectService();
+                if (socket == null) {
+                    Slog.e(TAG, "Failed to connect to uncrypt socket");
+                    return false;
+                }
+
+                // Read the status from the socket.
+                DataInputStream dis = null;
+                DataOutputStream dos = null;
+                try {
+                    dis = new DataInputStream(socket.getInputStream());
+                    dos = new DataOutputStream(socket.getOutputStream());
+                    int lastStatus = Integer.MIN_VALUE;
+                    while (true) {
+                        int status = dis.readInt();
+                        // Avoid flooding the log with the same message.
+                        if (status == lastStatus && lastStatus != Integer.MIN_VALUE) {
+                            continue;
+                        }
+                        lastStatus = status;
+
+                        if (status >= 0 && status <= 100) {
+                            // Update status
+                            Slog.i(TAG, "uncrypt read status: " + status);
+                            if (listener != null) {
+                                try {
+                                    listener.onProgress(status);
+                                } catch (RemoteException ignored) {
+                                    Slog.w(TAG, "RemoteException when posting progress");
+                                }
+                            }
+                            if (status == 100) {
+                                Slog.i(TAG, "uncrypt successfully finished.");
+                                // Ack receipt of the final status code. uncrypt
+                                // waits for the ack so the socket won't be
+                                // destroyed before we receive the code.
+                                dos.writeInt(0);
+                                break;
+                            }
+                        } else {
+                            // Error in /system/bin/uncrypt.
+                            Slog.e(TAG, "uncrypt failed with status: " + status);
+                            // Ack receipt of the final status code. uncrypt waits
+                            // for the ack so the socket won't be destroyed before
+                            // we receive the code.
+                            dos.writeInt(0);
+                            return false;
+                        }
+                    }
+                } catch (IOException e) {
+                    Slog.e(TAG, "IOException when reading status: ", e);
+                    return false;
+                } finally {
+                    IoUtils.closeQuietly(dis);
+                    IoUtils.closeQuietly(dos);
+                    IoUtils.closeQuietly(socket);
+                }
+
+                return true;
+            }
         }
 
         @Override // Binder call
         public boolean clearBcb() {
             if (DEBUG) Slog.d(TAG, "clearBcb");
-            return setupOrClearBcb(false, null);
+            synchronized (sRequestLock) {
+                return setupOrClearBcb(false, null);
+            }
         }
 
         @Override // Binder call
         public boolean setupBcb(String command) {
             if (DEBUG) Slog.d(TAG, "setupBcb: [" + command + "]");
-            return setupOrClearBcb(true, command);
+            synchronized (sRequestLock) {
+                return setupOrClearBcb(true, command);
+            }
+        }
+
+        @Override // Binder call
+        public void rebootRecoveryWithCommand(String command) {
+            if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
+            synchronized (sRequestLock) {
+                if (!setupOrClearBcb(true, command)) {
+                    return;
+                }
+
+                // Having set up the BCB, go ahead and reboot.
+                PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+                pm.reboot(PowerManager.REBOOT_RECOVERY);
+            }
+        }
+
+        /**
+         * Check if any of the init services is still running. If so, we cannot
+         * start a new uncrypt/setup-bcb/clear-bcb service right away; otherwise
+         * it may break the socket communication since init creates / deletes
+         * the socket (/dev/socket/uncrypt) on service start / exit.
+         */
+        private boolean checkAndWaitForUncryptService() {
+            for (int retry = 0; retry < SOCKET_CONNECTION_MAX_RETRY; retry++) {
+                final String uncryptService = SystemProperties.get(INIT_SERVICE_UNCRYPT);
+                final String setupBcbService = SystemProperties.get(INIT_SERVICE_SETUP_BCB);
+                final String clearBcbService = SystemProperties.get(INIT_SERVICE_CLEAR_BCB);
+                final boolean busy = "running".equals(uncryptService) ||
+                        "running".equals(setupBcbService) || "running".equals(clearBcbService);
+                if (DEBUG) {
+                    Slog.i(TAG, "retry: " + retry + " busy: " + busy +
+                            " uncrypt: [" + uncryptService + "]" +
+                            " setupBcb: [" + setupBcbService + "]" +
+                            " clearBcb: [" + clearBcbService + "]");
+                }
+
+                if (!busy) {
+                    return true;
+                }
+
+                try {
+                    Thread.sleep(1000);
+                } catch (InterruptedException e) {
+                    Slog.w(TAG, "Interrupted:", e);
+                }
+            }
+
+            return false;
         }
 
         private LocalSocket connectService() {
@@ -176,7 +244,7 @@
                     try {
                         Thread.sleep(1000);
                     } catch (InterruptedException e) {
-                        Slog.w(TAG, "Interrupted: ", e);
+                        Slog.w(TAG, "Interrupted:", e);
                     }
                 }
             }
@@ -190,6 +258,12 @@
         private boolean setupOrClearBcb(boolean isSetup, String command) {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
 
+            final boolean available = checkAndWaitForUncryptService();
+            if (!available) {
+                Slog.e(TAG, "uncrypt service is unavailable.");
+                return false;
+            }
+
             if (isSetup) {
                 SystemProperties.set("ctl.start", "setup-bcb");
             } else {
@@ -232,7 +306,7 @@
                     return false;
                 }
             } catch (IOException e) {
-                Slog.e(TAG, "IOException when communicating with uncrypt: ", e);
+                Slog.e(TAG, "IOException when communicating with uncrypt:", e);
                 return false;
             } finally {
                 IoUtils.closeQuietly(dis);
diff --git a/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java b/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java
index 63afccc..c3b7e15 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerBackupHelper.java
@@ -142,9 +142,8 @@
         final AccountManagerService.UserAccounts accounts = mAccountManagerService
                 .getUserAccounts(userId);
         synchronized (accounts.cacheLock) {
-            SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
-            List<Pair<String, Integer>> allAccountGrants = DeDatabaseHelper.findAllAccountGrants(
-                    db);
+            List<Pair<String, Integer>> allAccountGrants = accounts.accountsDb
+                    .findAllAccountGrants();
             if (allAccountGrants.isEmpty()) {
                 return null;
             }
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 575018d..7802576 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -98,9 +98,6 @@
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
-import com.android.server.accounts.AccountsDb.CeDatabaseHelper;
-import com.android.server.accounts.AccountsDb.DeDatabaseHelper;
-import com.android.server.accounts.AccountsDb.DebugDbHelper;
 
 import com.google.android.collect.Lists;
 import com.google.android.collect.Sets;
@@ -161,8 +158,6 @@
         }
     }
 
-    private static final int MAX_DEBUG_DB_SIZE = 64;
-
     final Context mContext;
 
     private final PackageManager mPackageManager;
@@ -193,7 +188,7 @@
 
     static class UserAccounts {
         private final int userId;
-        final DeDatabaseHelper openHelper;
+        final AccountsDb accountsDb;
         private final HashMap<Pair<Pair<Account, String>, Integer>, Integer>
                 credentialsPermissionNotificationIds =
                 new HashMap<Pair<Pair<Account, String>, Integer>, Integer>();
@@ -242,12 +237,12 @@
                 new HashMap<Account, AtomicReference<String>>();
 
         private int debugDbInsertionPoint = -1;
-        private SQLiteStatement statementForLogging;
+        private SQLiteStatement statementForLogging; // TODO Move to AccountsDb
 
         UserAccounts(Context context, int userId, File preNDbFile, File deDbFile) {
             this.userId = userId;
             synchronized (cacheLock) {
-                openHelper = DeDatabaseHelper.create(context, userId, preNDbFile, deDbFile);
+                accountsDb = AccountsDb.create(context, userId, preNDbFile, deDbFile);
             }
         }
     }
@@ -993,7 +988,7 @@
             UserAccounts accounts, boolean invalidateAuthenticatorCache) {
         if (Log.isLoggable(TAG, Log.DEBUG)) {
             Log.d(TAG, "validateAccountsInternal " + accounts.userId
-                    + " isCeDatabaseAttached=" + accounts.openHelper.isCeDatabaseAttached()
+                    + " isCeDatabaseAttached=" + accounts.accountsDb.isCeDatabaseAttached()
                     + " userLocked=" + mLocalUnlockedUsers.get(accounts.userId));
         }
 
@@ -1006,11 +1001,11 @@
         boolean userUnlocked = isLocalUnlockedUser(accounts.userId);
 
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
             boolean accountDeleted = false;
 
             // Get a map of stored authenticator types to UID
-            Map<String, Integer> metaAuthUid = DeDatabaseHelper.findMetaAuthUid(db);
+            final AccountsDb accountsDb = accounts.accountsDb;
+            Map<String, Integer> metaAuthUid = accountsDb.findMetaAuthUid();
             // Create a list of authenticator type whose previous uid no longer exists
             HashSet<String> obsoleteAuthType = Sets.newHashSet();
             SparseBooleanArray knownUids = null;
@@ -1047,7 +1042,7 @@
                         // So purge its data from the account databases.
                         obsoleteAuthType.add(type);
                         // And delete it from the TABLE_META
-                        DeDatabaseHelper.deleteMetaByAuthTypeAndUid(db, type, uid);
+                        accountsDb.deleteMetaByAuthTypeAndUid(type, uid);
                     }
                 }
             }
@@ -1056,11 +1051,10 @@
             // been re-enabled (after being updated for example), then we just overwrite the old
             // values.
             for (Entry<String, Integer> entry : knownAuth.entrySet()) {
-                DeDatabaseHelper.insertOrReplaceMetaAuthTypeAndUid(db, entry.getKey(),
-                        entry.getValue());
+                accountsDb.insertOrReplaceMetaAuthTypeAndUid(entry.getKey(), entry.getValue());
             }
 
-            final Map<Long, Account> accountsMap = DeDatabaseHelper.findAllAccounts(db);
+            final Map<Long, Account> accountsMap = accountsDb.findAllDeAccounts();
             try {
                 accounts.accountCache.clear();
                 final HashMap<String, ArrayList<String>> accountNamesByType = new LinkedHashMap<>();
@@ -1070,17 +1064,17 @@
                     if (obsoleteAuthType.contains(account.type)) {
                         Slog.w(TAG, "deleting account " + account.name + " because type "
                                 + account.type + "'s registered authenticator no longer exist.");
-                        db.beginTransaction();
+                        accountsDb.beginTransaction();
                         try {
-                            DeDatabaseHelper.deleteAccount(db, accountId);
+                            accountsDb.deleteDeAccount(accountId);
                             // Also delete from CE table if user is unlocked; if user is currently
                             // locked the account will be removed later by syncDeCeAccountsLocked
                             if (userUnlocked) {
-                                AccountsDb.deleteCeAccount(db, accountId);
+                                accountsDb.deleteCeAccount(accountId);
                             }
-                            db.setTransactionSuccessful();
+                            accountsDb.setTransactionSuccessful();
                         } finally {
-                            db.endTransaction();
+                            accountsDb.endTransaction();
                         }
                         accountDeleted = true;
 
@@ -1163,20 +1157,17 @@
                 File preNDbFile = new File(mInjector.getPreNDatabaseName(userId));
                 File deDbFile = new File(mInjector.getDeDatabaseName(userId));
                 accounts = new UserAccounts(mContext, userId, preNDbFile, deDbFile);
-                initializeDebugDbSizeAndCompileSqlStatementForLogging(
-                        accounts.openHelper.getWritableDatabase(), accounts);
+                initializeDebugDbSizeAndCompileSqlStatementForLogging(accounts);
                 mUsers.append(userId, accounts);
                 purgeOldGrants(accounts);
                 validateAccounts = true;
             }
             // open CE database if necessary
-            if (!accounts.openHelper.isCeDatabaseAttached() && mLocalUnlockedUsers.get(userId)) {
+            if (!accounts.accountsDb.isCeDatabaseAttached() && mLocalUnlockedUsers.get(userId)) {
                 Log.i(TAG, "User " + userId + " is unlocked - opening CE database");
                 synchronized (accounts.cacheLock) {
-                    File preNDatabaseFile = new File(mInjector.getPreNDatabaseName(userId));
                     File ceDatabaseFile = new File(mInjector.getCeDatabaseName(userId));
-                    CeDatabaseHelper.create(mContext, userId, preNDatabaseFile, ceDatabaseFile);
-                    accounts.openHelper.attachCeDatabase(ceDatabaseFile);
+                    accounts.accountsDb.attachCeDatabase(ceDatabaseFile);
                 }
                 syncDeCeAccountsLocked(accounts);
             }
@@ -1189,8 +1180,7 @@
 
     private void syncDeCeAccountsLocked(UserAccounts accounts) {
         Preconditions.checkState(Thread.holdsLock(mUsers), "mUsers lock must be held");
-        final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
-        List<Account> accountsToRemove = AccountsDb.findCeAccountsNotInDe(db);
+        List<Account> accountsToRemove = accounts.accountsDb.findCeAccountsNotInDe();
         if (!accountsToRemove.isEmpty()) {
             Slog.i(TAG, "Accounts " + accountsToRemove + " were previously deleted while user "
                     + accounts.userId + " was locked. Removing accounts from CE tables");
@@ -1213,8 +1203,7 @@
 
     private void purgeOldGrants(UserAccounts accounts) {
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
-            List<Integer> uids = DeDatabaseHelper.findAllUidGrants(db);
+            List<Integer> uids = accounts.accountsDb.findAllUidGrants();
             for (int uid : uids) {
                 final boolean packageExists = mPackageManager.getPackagesForUid(uid) != null;
                 if (packageExists) {
@@ -1222,7 +1211,7 @@
                 }
                 Log.d(TAG, "deleting grants for UID " + uid
                         + " because its package is no longer installed");
-                DeDatabaseHelper.deleteGrantsByUid(db, uid);
+                accounts.accountsDb.deleteGrantsByUid(uid);
             }
         }
     }
@@ -1241,7 +1230,7 @@
         }
         if (accounts != null) {
             synchronized (accounts.cacheLock) {
-                accounts.openHelper.close();
+                accounts.accountsDb.close();
             }
         }
         Log.i(TAG, "Removing database files for user " + userId);
@@ -1334,9 +1323,7 @@
         }
 
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
-            return CeDatabaseHelper.findAccountPasswordByNameAndType(db, account.name,
-                    account.type);
+            return accounts.accountsDb.findAccountPasswordByNameAndType(account.name, account.type);
         }
     }
 
@@ -1365,8 +1352,7 @@
         synchronized (accounts.cacheLock) {
             AtomicReference<String> previousNameRef = accounts.previousNameCache.get(account);
             if (previousNameRef == null) {
-                final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
-                String previousName = DeDatabaseHelper.findAccountPreviousName(db, account);
+                String previousName = accounts.accountsDb.findDeAccountPreviousName(account);
                 previousNameRef = new AtomicReference<>(previousName);
                 accounts.previousNameCache.put(account, previousNameRef);
                 return previousName;
@@ -1602,8 +1588,7 @@
     private boolean updateLastAuthenticatedTime(Account account) {
         final UserAccounts accounts = getUserAccountsForCaller();
         synchronized (accounts.cacheLock) {
-            return DeDatabaseHelper.updateAccountLastAuthenticatedTime(
-                    accounts.openHelper.getWritableDatabase(), account);
+            return accounts.accountsDb.updateAccountLastAuthenticatedTime(account);
         }
     }
 
@@ -1672,22 +1657,21 @@
             return false;
         }
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
-            db.beginTransaction();
+            accounts.accountsDb.beginTransaction();
             try {
-                if (CeDatabaseHelper.findAccountId(db, account) >= 0) {
+                if (accounts.accountsDb.findCeAccountId(account) >= 0) {
                     Log.w(TAG, "insertAccountIntoDatabase: " + account
                             + ", skipping since the account already exists");
                     return false;
                 }
-                long accountId = CeDatabaseHelper.insertAccount(db, account, password);
+                long accountId = accounts.accountsDb.insertCeAccount(account, password);
                 if (accountId < 0) {
                     Log.w(TAG, "insertAccountIntoDatabase: " + account
                             + ", skipping the DB insert failed");
                     return false;
                 }
                 // Insert into DE table
-                if (DeDatabaseHelper.insertAccount(db, account, accountId) < 0) {
+                if (accounts.accountsDb.insertDeAccount(account, accountId) < 0) {
                     Log.w(TAG, "insertAccountIntoDatabase: " + account
                             + ", skipping the DB insert failed");
                     return false;
@@ -1695,21 +1679,21 @@
                 if (extras != null) {
                     for (String key : extras.keySet()) {
                         final String value = extras.getString(key);
-                        if (CeDatabaseHelper.insertExtra(db, accountId, key, value) < 0) {
+                        if (accounts.accountsDb.insertExtra(accountId, key, value) < 0) {
                             Log.w(TAG, "insertAccountIntoDatabase: " + account
                                     + ", skipping since insertExtra failed for key " + key);
                             return false;
                         }
                     }
                 }
-                db.setTransactionSuccessful();
+                accounts.accountsDb.setTransactionSuccessful();
 
                 logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_ADD, AccountsDb.TABLE_ACCOUNTS,
                         accountId, accounts, callingUid);
 
                 insertAccountIntoCacheLocked(accounts, account);
             } finally {
-                db.endTransaction();
+                accounts.accountsDb.endTransaction();
             }
         }
         if (getUserManager().getUserInfo(accounts.userId).canHaveProfile()) {
@@ -1892,17 +1876,16 @@
             }
         }
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
-            db.beginTransaction();
+            accounts.accountsDb.beginTransaction();
             Account renamedAccount = new Account(newName, accountToRename.type);
             try {
-                final long accountId = DeDatabaseHelper.findAccountId(db, accountToRename);
+                final long accountId = accounts.accountsDb.findDeAccountId(accountToRename);
                 if (accountId >= 0) {
-                    CeDatabaseHelper.renameAccount(db, accountId, newName);
-                    DeDatabaseHelper.renameAccount(db, accountId, newName, accountToRename.name);
+                    accounts.accountsDb.renameCeAccount(accountId, newName);
+                    accounts.accountsDb.renameDeAccount(accountId, newName, accountToRename.name);
                 }
             } finally {
-                db.endTransaction();
+                accounts.accountsDb.endTransaction();
             }
             /*
              * Database transaction was successful. Clean up cached
@@ -2025,8 +2008,7 @@
                 }
             }
         }
-        SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
-        final long accountId = DeDatabaseHelper.findAccountId(db, account);
+        final long accountId = accounts.accountsDb.findDeAccountId(account);
         logRecord(
                 AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_REMOVE,
                 AccountsDb.TABLE_ACCOUNTS,
@@ -2065,8 +2047,7 @@
         }
         removeVisibleListFunctionality(account, getUserAccounts(UserHandle.getUserId(callingUid)));
         UserAccounts accounts = getUserAccountsForCaller();
-        SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
-        final long accountId = DeDatabaseHelper.findAccountId(db, account);
+        final long accountId = accounts.accountsDb.findDeAccountId(account);
         logRecord(
                 AccountsDb.DEBUG_ACTION_CALLED_ACCOUNT_REMOVE,
                 AccountsDb.TABLE_ACCOUNTS,
@@ -2143,26 +2124,23 @@
                     + " is still locked. CE data will be removed later");
         }
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = userUnlocked
-                    ? accounts.openHelper.getWritableDatabaseUserIsUnlocked()
-                    : accounts.openHelper.getWritableDatabase();
-            db.beginTransaction();
+            accounts.accountsDb.beginTransaction();
             // Set to a dummy value, this will only be used if the database
             // transaction succeeds.
             long accountId = -1;
             try {
-                accountId = DeDatabaseHelper.findAccountId(db, account);
+                accountId = accounts.accountsDb.findDeAccountId(account);
                 if (accountId >= 0) {
-                    DeDatabaseHelper.deleteAccount(db, accountId);
+                    accounts.accountsDb.deleteDeAccount(accountId);
                     if (userUnlocked) {
                         // Delete from CE table
-                        AccountsDb.deleteCeAccount(db, accountId);
+                        accounts.accountsDb.deleteCeAccount(accountId);
                     }
-                    db.setTransactionSuccessful();
+                    accounts.accountsDb.setTransactionSuccessful();
                     isChanged = true;
                 }
             } finally {
-                db.endTransaction();
+                accounts.accountsDb.endTransaction();
             }
             if (isChanged) {
                 removeAccountFromCacheLocked(accounts, account);
@@ -2221,14 +2199,13 @@
         try {
             UserAccounts accounts = getUserAccounts(userId);
             synchronized (accounts.cacheLock) {
-                final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
-                db.beginTransaction();
+                accounts.accountsDb.beginTransaction();
                 try {
-                    invalidateAuthTokenLocked(accounts, db, accountType, authToken);
+                    invalidateAuthTokenLocked(accounts, accountType, authToken);
                     invalidateCustomTokenLocked(accounts, accountType, authToken);
-                    db.setTransactionSuccessful();
+                    accounts.accountsDb.setTransactionSuccessful();
                 } finally {
-                    db.endTransaction();
+                    accounts.accountsDb.endTransaction();
                 }
             }
         } finally {
@@ -2247,21 +2224,20 @@
         accounts.accountTokenCaches.remove(accountType, authToken);
     }
 
-    private void invalidateAuthTokenLocked(UserAccounts accounts, SQLiteDatabase db,
-            String accountType, String authToken) {
+    private void invalidateAuthTokenLocked(UserAccounts accounts, String accountType,
+            String authToken) {
         if (authToken == null || accountType == null) {
             return;
         }
-        Cursor cursor = CeDatabaseHelper.findAuthtokenForAllAccounts(db, accountType, authToken);
+        Cursor cursor = accounts.accountsDb.findAuthtokenForAllAccounts(accountType, authToken);
         try {
             while (cursor.moveToNext()) {
                 String authTokenId = cursor.getString(0);
                 String accountName = cursor.getString(1);
                 String authTokenType = cursor.getString(2);
-                CeDatabaseHelper.deleteAuthToken(db, authTokenId);
+                accounts.accountsDb.deleteAuthToken(authTokenId);
                 writeAuthTokenIntoCacheLocked(
                         accounts,
-                        db,
                         new Account(accountName, accountType),
                         authTokenType,
                         null);
@@ -2299,22 +2275,21 @@
         cancelNotification(getSigninRequiredNotificationId(accounts, account),
                 UserHandle.of(accounts.userId));
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
-            db.beginTransaction();
+            accounts.accountsDb.beginTransaction();
             try {
-                long accountId = DeDatabaseHelper.findAccountId(db, account);
+                long accountId = accounts.accountsDb.findDeAccountId(account);
                 if (accountId < 0) {
                     return false;
                 }
-                CeDatabaseHelper.deleteAuthtokensByAccountIdAndType(db, accountId, type);
-                if (CeDatabaseHelper.insertAuthToken(db, accountId, type, authToken) >= 0) {
-                    db.setTransactionSuccessful();
-                    writeAuthTokenIntoCacheLocked(accounts, db, account, type, authToken);
+                accounts.accountsDb.deleteAuthtokensByAccountIdAndType(accountId, type);
+                if (accounts.accountsDb.insertAuthToken(accountId, type, authToken) >= 0) {
+                    accounts.accountsDb.setTransactionSuccessful();
+                    writeAuthTokenIntoCacheLocked(accounts, account, type, authToken);
                     return true;
                 }
                 return false;
             } finally {
-                db.endTransaction();
+                accounts.accountsDb.endTransaction();
             }
         }
     }
@@ -2413,16 +2388,15 @@
         }
         boolean isChanged = false;
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabaseUserIsUnlocked();
-            db.beginTransaction();
+            accounts.accountsDb.beginTransaction();
             try {
-                final long accountId = DeDatabaseHelper.findAccountId(db, account);
+                final long accountId = accounts.accountsDb.findDeAccountId(account);
                 if (accountId >= 0) {
-                    CeDatabaseHelper.updateAccountPassword(db, accountId, password);
-                    CeDatabaseHelper.deleteAuthTokensByAccountId(db, accountId);
+                    accounts.accountsDb.updateCeAccountPassword(accountId, password);
+                    accounts.accountsDb.deleteAuthTokensByAccountId(accountId);
                     accounts.authTokenCache.remove(account);
                     accounts.accountTokenCaches.remove(account);
-                    db.setTransactionSuccessful();
+                    accounts.accountsDb.setTransactionSuccessful();
                     // If there is an account whose password will be updated and the database
                     // transactions succeed, then we say that a change has occured. Even if the
                     // new password is the same as the old and there were no authtokens to delete.
@@ -2433,7 +2407,7 @@
                     logRecord(action, AccountsDb.TABLE_ACCOUNTS, accountId, accounts, callingUid);
                 }
             } finally {
-                db.endTransaction();
+                accounts.accountsDb.endTransaction();
                 if (isChanged) {
                     // Send LOGIN_ACCOUNTS_CHANGED only if the something changed.
                     sendAccountsChangedBroadcast(accounts.userId);
@@ -2523,26 +2497,25 @@
         if (account == null || key == null) {
             return;
         }
-        final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
-        db.beginTransaction();
+        accounts.accountsDb.beginTransaction();
         try {
-            long accountId = DeDatabaseHelper.findAccountId(db, account);
+            long accountId = accounts.accountsDb.findDeAccountId(account);
             if (accountId < 0) {
                 return;
             }
-            long extrasId = CeDatabaseHelper.findExtrasIdByAccountId(db, accountId, key);
+            long extrasId = accounts.accountsDb.findExtrasIdByAccountId(accountId, key);
             if (extrasId < 0) {
-                extrasId = CeDatabaseHelper.insertExtra(db, accountId, key, value);
+                extrasId = accounts.accountsDb.insertExtra(accountId, key, value);
                 if (extrasId < 0) {
                     return;
                 }
-            } else if (!CeDatabaseHelper.updateExtra(db, extrasId, value)) {
+            } else if (!accounts.accountsDb.updateExtra(extrasId, value)) {
                 return;
             }
-            writeUserDataIntoCacheLocked(accounts, db, account, key, value);
-            db.setTransactionSuccessful();
+            writeUserDataIntoCacheLocked(accounts, account, key, value);
+            accounts.accountsDb.setTransactionSuccessful();
         } finally {
-            db.endTransaction();
+            accounts.accountsDb.endTransaction();
         }
     }
 
@@ -4171,9 +4144,8 @@
     private boolean addSharedAccountAsUser(Account account, int userId) {
         userId = handleIncomingUser(userId);
         UserAccounts accounts = getUserAccounts(userId);
-        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
-        DeDatabaseHelper.deleteSharedAccount(db, account);
-        long accountId = DeDatabaseHelper.insertSharedAccount(db, account);
+        accounts.accountsDb.deleteSharedAccount(account);
+        long accountId = accounts.accountsDb.insertSharedAccount(account);
         if (accountId < 0) {
             Log.w(TAG, "insertAccountIntoDatabase: " + account
                     + ", skipping the DB insert failed");
@@ -4188,9 +4160,8 @@
     public boolean renameSharedAccountAsUser(Account account, String newName, int userId) {
         userId = handleIncomingUser(userId);
         UserAccounts accounts = getUserAccounts(userId);
-        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
-        long sharedTableAccountId = DeDatabaseHelper.findSharedAccountId(db, account);
-        int r = DeDatabaseHelper.renameSharedAccount(db, account, newName);
+        long sharedTableAccountId = accounts.accountsDb.findSharedAccountId(account);
+        int r = accounts.accountsDb.renameSharedAccount(account, newName);
         if (r > 0) {
             int callingUid = getCallingUid();
             logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_RENAME, AccountsDb.TABLE_SHARED_ACCOUNTS,
@@ -4209,9 +4180,8 @@
     private boolean removeSharedAccountAsUser(Account account, int userId, int callingUid) {
         userId = handleIncomingUser(userId);
         UserAccounts accounts = getUserAccounts(userId);
-        SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
-        long sharedTableAccountId = DeDatabaseHelper.findSharedAccountId(db, account);
-        boolean deleted = DeDatabaseHelper.deleteSharedAccount(db, account);
+        long sharedTableAccountId = accounts.accountsDb.findSharedAccountId(account);
+        boolean deleted = accounts.accountsDb.deleteSharedAccount(account);
         if (deleted) {
             logRecord(AccountsDb.DEBUG_ACTION_ACCOUNT_REMOVE, AccountsDb.TABLE_SHARED_ACCOUNTS,
                     sharedTableAccountId, accounts, callingUid);
@@ -4223,8 +4193,8 @@
     @Override
     public Account[] getSharedAccountsAsUser(int userId) {
         userId = handleIncomingUser(userId);
-        SQLiteDatabase db = getUserAccounts(userId).openHelper.getReadableDatabase();
-        List<Account> accountList = DeDatabaseHelper.getSharedAccounts(db);
+        UserAccounts accounts = getUserAccounts(userId);
+        List<Account> accountList = accounts.accountsDb.getSharedAccounts();
         Account[] accountArray = new Account[accountList.size()];
         accountList.toArray(accountArray);
         return accountArray;
@@ -4575,9 +4545,8 @@
                     if (mAuthDetailsRequired) {
                         long lastAuthenticatedTime = -1;
                         if (accountPresent) {
-                            lastAuthenticatedTime = DeDatabaseHelper
+                            lastAuthenticatedTime = mAccounts.accountsDb
                                     .findAccountLastAuthenticatedTime(
-                                            mAccounts.openHelper.getReadableDatabase(),
                                             new Account(mAccountName, mAccountType));
                         }
                         result.putLong(AccountManager.KEY_LAST_AUTHENTICATED_TIME,
@@ -4795,7 +4764,7 @@
         LogRecordTask logTask = new LogRecordTask(action, tableName, accountId, userAccount,
                 callingUid, userAccount.debugDbInsertionPoint);
         userAccount.debugDbInsertionPoint = (userAccount.debugDbInsertionPoint + 1)
-                % MAX_DEBUG_DB_SIZE;
+                % AccountsDb.MAX_DEBUG_DB_SIZE;
         mHandler.post(logTask);
     }
 
@@ -4803,17 +4772,10 @@
      * This should only be called once to compile the sql statement for logging
      * and to find the insertion point.
      */
-    private void initializeDebugDbSizeAndCompileSqlStatementForLogging(SQLiteDatabase db,
-            UserAccounts userAccount) {
-        // Initialize the count if not done earlier.
-        int size = DebugDbHelper.getDebugTableRowCount(db);
-        if (size >= MAX_DEBUG_DB_SIZE) {
-            // Table is full, and we need to find the point where to insert.
-            userAccount.debugDbInsertionPoint = DebugDbHelper.getDebugTableInsertionPoint(db);
-        } else {
-            userAccount.debugDbInsertionPoint = size;
-        }
-        userAccount.statementForLogging = DebugDbHelper.compileSqlStatementForLogging(db);
+    private void initializeDebugDbSizeAndCompileSqlStatementForLogging(UserAccounts userAccount) {
+        userAccount.debugDbInsertionPoint = userAccount.accountsDb
+                .calculateDebugTableInsertionPoint();
+        userAccount.statementForLogging = userAccount.accountsDb.compileSqlStatementForLogging();
     }
 
     public IBinder onBind(@SuppressWarnings("unused") Intent intent) {
@@ -4862,11 +4824,9 @@
     private void dumpUser(UserAccounts userAccounts, FileDescriptor fd, PrintWriter fout,
             String[] args, boolean isCheckinRequest) {
         synchronized (userAccounts.cacheLock) {
-            final SQLiteDatabase db = userAccounts.openHelper.getReadableDatabase();
-
             if (isCheckinRequest) {
                 // This is a checkin request. *Only* upload the account types and the count of each.
-                DeDatabaseHelper.dumpAccountsTable(db, fout);
+                userAccounts.accountsDb.dumpDeAccountsTable(fout);
             } else {
                 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
                         Process.myUid(), null);
@@ -4877,7 +4837,7 @@
 
                 // Add debug information.
                 fout.println();
-                DebugDbHelper.dumpDebugTable(db, fout);
+                userAccounts.accountsDb.dumpDebugTable(fout);
                 fout.println();
                 synchronized (mSessions) {
                     final long now = SystemClock.elapsedRealtime();
@@ -5139,13 +5099,12 @@
         }
         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callerUid));
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getReadableDatabase();
             long grantsCount;
             if (authTokenType != null) {
-                grantsCount = DeDatabaseHelper.findMatchingGrantsCount(db, callerUid, authTokenType,
+                grantsCount = accounts.accountsDb.findMatchingGrantsCount(callerUid, authTokenType,
                         account);
             } else {
-                grantsCount = DeDatabaseHelper.findMatchingGrantsCountAnyToken(db, callerUid,
+                grantsCount = accounts.accountsDb.findMatchingGrantsCountAnyToken(callerUid,
                         account);
             }
             final boolean permissionGranted = grantsCount > 0;
@@ -5273,10 +5232,9 @@
         }
         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
-            long accountId = DeDatabaseHelper.findAccountId(db, account);
+            long accountId = accounts.accountsDb.findDeAccountId(account);
             if (accountId >= 0) {
-                DeDatabaseHelper.insertGrant(db, accountId, authTokenType, uid);
+                accounts.accountsDb.insertGrant(accountId, authTokenType, uid);
             }
             cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
                     UserHandle.of(accounts.userId));
@@ -5306,17 +5264,16 @@
         }
         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid));
         synchronized (accounts.cacheLock) {
-            final SQLiteDatabase db = accounts.openHelper.getWritableDatabase();
-            db.beginTransaction();
+            accounts.accountsDb.beginTransaction();
             try {
-                long accountId = DeDatabaseHelper.findAccountId(db, account);
+                long accountId = accounts.accountsDb.findDeAccountId(account);
                 if (accountId >= 0) {
-                    DeDatabaseHelper.deleteGrantsByAccountIdAuthTokenTypeAndUid(
-                            db, accountId, authTokenType, uid);
-                    db.setTransactionSuccessful();
+                    accounts.accountsDb.deleteGrantsByAccountIdAuthTokenTypeAndUid(
+                            accountId, authTokenType, uid);
+                    accounts.accountsDb.setTransactionSuccessful();
                 }
             } finally {
-                db.endTransaction();
+                accounts.accountsDb.endTransaction();
             }
 
             cancelNotification(getCredentialPermissionNotificationId(account, authTokenType, uid),
@@ -5472,11 +5429,11 @@
         }
     }
 
-    protected void writeUserDataIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
+    protected void writeUserDataIntoCacheLocked(UserAccounts accounts,
             Account account, String key, String value) {
         Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
         if (userDataForAccount == null) {
-            userDataForAccount = CeDatabaseHelper.findUserExtrasForAccount(db, account);
+            userDataForAccount = accounts.accountsDb.findUserExtrasForAccount(account);
             accounts.userDataCache.put(account, userDataForAccount);
         }
         if (value == null) {
@@ -5498,11 +5455,11 @@
         }
     }
 
-    protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts, final SQLiteDatabase db,
+    protected void writeAuthTokenIntoCacheLocked(UserAccounts accounts,
             Account account, String key, String value) {
         Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
         if (authTokensForAccount == null) {
-            authTokensForAccount = CeDatabaseHelper.findAuthTokensByAccount(db, account);
+            authTokensForAccount = accounts.accountsDb.findAuthTokensByAccount(account);
             accounts.authTokenCache.put(account, authTokensForAccount);
         }
         if (value == null) {
@@ -5518,8 +5475,7 @@
             Map<String, String> authTokensForAccount = accounts.authTokenCache.get(account);
             if (authTokensForAccount == null) {
                 // need to populate the cache for this account
-                final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
-                authTokensForAccount = CeDatabaseHelper.findAuthTokensByAccount(db, account);
+                authTokensForAccount = accounts.accountsDb.findAuthTokensByAccount(account);
                 accounts.authTokenCache.put(account, authTokensForAccount);
             }
             return authTokensForAccount.get(authTokenType);
@@ -5531,8 +5487,7 @@
         Map<String, String> userDataForAccount = accounts.userDataCache.get(account);
         if (userDataForAccount == null) {
             // need to populate the cache for this account
-            final SQLiteDatabase db = accounts.openHelper.getReadableDatabaseUserIsUnlocked();
-            userDataForAccount = CeDatabaseHelper.findUserExtrasForAccount(db, account);
+            userDataForAccount = accounts.accountsDb.findUserExtrasForAccount(account);
             accounts.userDataCache.put(account, userDataForAccount);
         }
         return userDataForAccount.get(key);
diff --git a/services/core/java/com/android/server/accounts/AccountsDb.java b/services/core/java/com/android/server/accounts/AccountsDb.java
index 6ef521e..cb594f6 100644
--- a/services/core/java/com/android/server/accounts/AccountsDb.java
+++ b/services/core/java/com/android/server/accounts/AccountsDb.java
@@ -42,8 +42,13 @@
 
 /**
  * Persistence layer abstraction for accessing accounts_ce/accounts_de databases.
+ *
+ * <p>At first, CE database needs to be {@link #attachCeDatabase(File) attached to DE},
+ * in order for the tables to be available. All operations with CE database are done through the
+ * connection to the DE database, to which it is attached. This approach allows atomic
+ * transactions across two databases</p>
  */
-class AccountsDb {
+class AccountsDb implements AutoCloseable {
     private static final String TAG = "AccountsDb";
 
     private static final String DATABASE_NAME = "accounts.db";
@@ -128,6 +133,8 @@
     private static final String CE_TABLE_AUTHTOKENS = CE_DB_PREFIX + TABLE_AUTHTOKENS;
     private static final String CE_TABLE_EXTRAS = CE_DB_PREFIX + TABLE_EXTRAS;
 
+    static final int MAX_DEBUG_DB_SIZE = 64;
+
     private static final String[] ACCOUNT_TYPE_COUNT_PROJECTION =
             new String[] { ACCOUNTS_TYPE, ACCOUNTS_TYPE_COUNT};
 
@@ -169,7 +176,17 @@
     private static final String META_KEY_DELIMITER = ":";
     private static final String SELECTION_META_BY_AUTHENTICATOR_TYPE = META_KEY + " LIKE ?";
 
-    static class CeDatabaseHelper extends SQLiteOpenHelper {
+    private final DeDatabaseHelper mDeDatabase;
+    private final Context mContext;
+    private final File mPreNDatabaseFile;
+
+    AccountsDb(DeDatabaseHelper deDatabase, Context context, File preNDatabaseFile) {
+        mDeDatabase = deDatabase;
+        mContext = context;
+        mPreNDatabaseFile = preNDatabaseFile;
+    }
+
+    private static class CeDatabaseHelper extends SQLiteOpenHelper {
 
         CeDatabaseHelper(Context context, String ceDatabaseName) {
             super(context, ceDatabaseName, null, CE_DATABASE_VERSION);
@@ -249,17 +266,16 @@
         /**
          * Creates a new {@code CeDatabaseHelper}. If pre-N db file is present at the old location,
          * it also performs migration to the new CE database.
-         * @param userId id of the user where the database is located
          */
         static CeDatabaseHelper create(
                 Context context,
-                int userId,
                 File preNDatabaseFile,
                 File ceDatabaseFile) {
             boolean newDbExists = ceDatabaseFile.exists();
             if (Log.isLoggable(TAG, Log.VERBOSE)) {
-                Log.v(TAG, "CeDatabaseHelper.create userId=" + userId + " oldDbExists="
-                        + preNDatabaseFile.exists() + " newDbExists=" + newDbExists);
+                Log.v(TAG, "CeDatabaseHelper.create ceDatabaseFile=" + ceDatabaseFile
+                        + " oldDbExists=" + preNDatabaseFile.exists()
+                        + " newDbExists=" + newDbExists);
             }
             boolean removeOldDb = false;
             if (!newDbExists && preNDatabaseFile.exists()) {
@@ -290,187 +306,189 @@
             }
             return true;
         }
-
-        /**
-         * Returns information about auth tokens and their account for the specified query
-         * parameters.
-         * Output is in the format:
-         * <pre><code> | AUTHTOKEN_ID |  ACCOUNT_NAME | AUTH_TOKEN_TYPE |</code></pre>
-         */
-        static Cursor findAuthtokenForAllAccounts(SQLiteDatabase db, String accountType,
-                String authToken) {
-            return db.rawQuery(
-                    "SELECT " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
-                            + ", " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
-                            + ", " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
-                            + " FROM " + CE_TABLE_ACCOUNTS
-                            + " JOIN " + CE_TABLE_AUTHTOKENS
-                            + " ON " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
-                            + " = " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ACCOUNTS_ID
-                            + " WHERE " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_AUTHTOKEN
-                            + " = ? AND " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
-                    new String[]{authToken, accountType});
-        }
-
-        static boolean deleteAuthtokensByAccountIdAndType(SQLiteDatabase db, long accountId,
-                String authtokenType) {
-            return db.delete(CE_TABLE_AUTHTOKENS,
-                    AUTHTOKENS_ACCOUNTS_ID + "=?" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
-                    new String[]{String.valueOf(accountId), authtokenType}) > 0;
-        }
-
-        static boolean deleteAuthToken(SQLiteDatabase db, String authTokenId) {
-            return db.delete(
-                    CE_TABLE_AUTHTOKENS, AUTHTOKENS_ID + "= ?",
-                    new String[]{authTokenId}) > 0;
-        }
-
-        static long insertAuthToken(SQLiteDatabase db, long accountId, String authTokenType,
-                String authToken) {
-            ContentValues values = new ContentValues();
-            values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
-            values.put(AUTHTOKENS_TYPE, authTokenType);
-            values.put(AUTHTOKENS_AUTHTOKEN, authToken);
-            return db.insert(
-                    CE_TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values);
-        }
-
-        static Map<String, String> findAuthTokensByAccount(final SQLiteDatabase db,
-                Account account) {
-            HashMap<String, String> authTokensForAccount = new HashMap<>();
-            Cursor cursor = db.query(CE_TABLE_AUTHTOKENS,
-                    COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
-                    SELECTION_AUTHTOKENS_BY_ACCOUNT,
-                    new String[]{account.name, account.type},
-                    null, null, null);
-            try {
-                while (cursor.moveToNext()) {
-                    final String type = cursor.getString(0);
-                    final String authToken = cursor.getString(1);
-                    authTokensForAccount.put(type, authToken);
-                }
-            } finally {
-                cursor.close();
-            }
-            return authTokensForAccount;
-        }
-
-        static int updateAccountPassword(SQLiteDatabase db, long accountId, String password) {
-            final ContentValues values = new ContentValues();
-            values.put(ACCOUNTS_PASSWORD, password);
-            return db.update(
-                    CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?",
-                    new String[]{String.valueOf(accountId)});
-        }
-
-        static boolean renameAccount(SQLiteDatabase db, long accountId, String newName) {
-            final ContentValues values = new ContentValues();
-            values.put(ACCOUNTS_NAME, newName);
-            final String[] argsAccountId = {String.valueOf(accountId)};
-            return db.update(
-                    CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId) > 0;
-        }
-
-        static boolean deleteAuthTokensByAccountId(SQLiteDatabase db, long accountId) {
-            return db.delete(
-                    CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?",
-                    new String[]{String.valueOf(accountId)}) > 0;
-        }
-
-        static long findExtrasIdByAccountId(SQLiteDatabase db, long accountId, String key) {
-            Cursor cursor = db.query(
-                    CE_TABLE_EXTRAS, new String[]{EXTRAS_ID},
-                    EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
-                    new String[]{key}, null, null, null);
-            try {
-                if (cursor.moveToNext()) {
-                    return cursor.getLong(0);
-                }
-                return -1;
-            } finally {
-                cursor.close();
-            }
-        }
-
-        static boolean updateExtra(SQLiteDatabase db, long extrasId, String value) {
-            ContentValues values = new ContentValues();
-            values.put(EXTRAS_VALUE, value);
-            int rows = db.update(
-                    TABLE_EXTRAS, values, EXTRAS_ID + "=?",
-                    new String[]{String.valueOf(extrasId)});
-            return rows == 1;
-        }
-
-        static long insertExtra(SQLiteDatabase db, long accountId, String key, String value) {
-            ContentValues values = new ContentValues();
-            values.put(EXTRAS_KEY, key);
-            values.put(EXTRAS_ACCOUNTS_ID, accountId);
-            values.put(EXTRAS_VALUE, value);
-            return db.insert(CE_TABLE_EXTRAS, EXTRAS_KEY, values);
-        }
-
-        static Map<String, String> findUserExtrasForAccount(SQLiteDatabase db, Account account) {
-            Map<String, String> userExtrasForAccount = new HashMap<>();
-            Cursor cursor = db.query(CE_TABLE_EXTRAS,
-                    COLUMNS_EXTRAS_KEY_AND_VALUE,
-                    SELECTION_USERDATA_BY_ACCOUNT,
-                    new String[]{account.name, account.type},
-                    null, null, null);
-            try {
-                while (cursor.moveToNext()) {
-                    final String tmpkey = cursor.getString(0);
-                    final String value = cursor.getString(1);
-                    userExtrasForAccount.put(tmpkey, value);
-                }
-            } finally {
-                cursor.close();
-            }
-            return userExtrasForAccount;
-        }
-
-        static long findAccountId(SQLiteDatabase db, Account account) {
-            Cursor cursor = db.query(
-                    CE_TABLE_ACCOUNTS, new String[]{
-                            ACCOUNTS_ID},
-                    "name=? AND type=?", new String[]{account.name, account.type}, null, null,
-                    null);
-            try {
-                if (cursor.moveToNext()) {
-                    return cursor.getLong(0);
-                }
-                return -1;
-            } finally {
-                cursor.close();
-            }
-        }
-
-        static String findAccountPasswordByNameAndType(SQLiteDatabase db, String name,
-                String type) {
-            Cursor cursor = db.query(CE_TABLE_ACCOUNTS, new String[]{
-                            ACCOUNTS_PASSWORD},
-                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
-                    new String[]{name, type}, null, null, null);
-            try {
-                if (cursor.moveToNext()) {
-                    return cursor.getString(0);
-                }
-                return null;
-            } finally {
-                cursor.close();
-            }
-        }
-
-        static long insertAccount(SQLiteDatabase db, Account account, String password) {
-            ContentValues values = new ContentValues();
-            values.put(ACCOUNTS_NAME, account.name);
-            values.put(ACCOUNTS_TYPE, account.type);
-            values.put(ACCOUNTS_PASSWORD, password);
-            return db.insert(
-                    CE_TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
-        }
-
     }
 
+    /**
+     * Returns information about auth tokens and their account for the specified query
+     * parameters.
+     * Output is in the format:
+     * <pre><code> | AUTHTOKEN_ID |  ACCOUNT_NAME | AUTH_TOKEN_TYPE |</code></pre>
+     */
+    Cursor findAuthtokenForAllAccounts(String accountType, String authToken) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked();
+        return db.rawQuery(
+                "SELECT " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ID
+                        + ", " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_NAME
+                        + ", " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_TYPE
+                        + " FROM " + CE_TABLE_ACCOUNTS
+                        + " JOIN " + CE_TABLE_AUTHTOKENS
+                        + " ON " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_ID
+                        + " = " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_ACCOUNTS_ID
+                        + " WHERE " + CE_TABLE_AUTHTOKENS + "." + AUTHTOKENS_AUTHTOKEN
+                        + " = ? AND " + CE_TABLE_ACCOUNTS + "." + ACCOUNTS_TYPE + " = ?",
+                new String[]{authToken, accountType});
+    }
+
+    Map<String, String> findAuthTokensByAccount(Account account) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked();
+        HashMap<String, String> authTokensForAccount = new HashMap<>();
+        Cursor cursor = db.query(CE_TABLE_AUTHTOKENS,
+                COLUMNS_AUTHTOKENS_TYPE_AND_AUTHTOKEN,
+                SELECTION_AUTHTOKENS_BY_ACCOUNT,
+                new String[] {account.name, account.type},
+                null, null, null);
+        try {
+            while (cursor.moveToNext()) {
+                final String type = cursor.getString(0);
+                final String authToken = cursor.getString(1);
+                authTokensForAccount.put(type, authToken);
+            }
+        } finally {
+            cursor.close();
+        }
+        return authTokensForAccount;
+    }
+
+    boolean deleteAuthtokensByAccountIdAndType(long accountId, String authtokenType) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
+        return db.delete(CE_TABLE_AUTHTOKENS,
+                AUTHTOKENS_ACCOUNTS_ID + "=?" + accountId + " AND " + AUTHTOKENS_TYPE + "=?",
+                new String[]{String.valueOf(accountId), authtokenType}) > 0;
+    }
+
+    boolean deleteAuthToken(String authTokenId) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
+        return db.delete(
+                CE_TABLE_AUTHTOKENS, AUTHTOKENS_ID + "= ?",
+                new String[]{authTokenId}) > 0;
+    }
+
+    long insertAuthToken(long accountId, String authTokenType, String authToken) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
+        ContentValues values = new ContentValues();
+        values.put(AUTHTOKENS_ACCOUNTS_ID, accountId);
+        values.put(AUTHTOKENS_TYPE, authTokenType);
+        values.put(AUTHTOKENS_AUTHTOKEN, authToken);
+        return db.insert(
+                CE_TABLE_AUTHTOKENS, AUTHTOKENS_AUTHTOKEN, values);
+    }
+
+    int updateCeAccountPassword(long accountId, String password) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
+        final ContentValues values = new ContentValues();
+        values.put(ACCOUNTS_PASSWORD, password);
+        return db.update(
+                CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?",
+                new String[] {String.valueOf(accountId)});
+    }
+
+    boolean renameCeAccount(long accountId, String newName) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
+        final ContentValues values = new ContentValues();
+        values.put(ACCOUNTS_NAME, newName);
+        final String[] argsAccountId = {String.valueOf(accountId)};
+        return db.update(
+                CE_TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId) > 0;
+    }
+
+    boolean deleteAuthTokensByAccountId(long accountId) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
+        return db.delete(CE_TABLE_AUTHTOKENS, AUTHTOKENS_ACCOUNTS_ID + "=?",
+                new String[] {String.valueOf(accountId)}) > 0;
+    }
+
+    long findExtrasIdByAccountId(long accountId, String key) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked();
+        Cursor cursor = db.query(
+                CE_TABLE_EXTRAS, new String[]{EXTRAS_ID},
+                EXTRAS_ACCOUNTS_ID + "=" + accountId + " AND " + EXTRAS_KEY + "=?",
+                new String[]{key}, null, null, null);
+        try {
+            if (cursor.moveToNext()) {
+                return cursor.getLong(0);
+            }
+            return -1;
+        } finally {
+            cursor.close();
+        }
+    }
+
+    boolean updateExtra(long extrasId, String value) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
+        ContentValues values = new ContentValues();
+        values.put(EXTRAS_VALUE, value);
+        int rows = db.update(
+                TABLE_EXTRAS, values, EXTRAS_ID + "=?",
+                new String[]{String.valueOf(extrasId)});
+        return rows == 1;
+    }
+
+    long insertExtra(long accountId, String key, String value) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
+        ContentValues values = new ContentValues();
+        values.put(EXTRAS_KEY, key);
+        values.put(EXTRAS_ACCOUNTS_ID, accountId);
+        values.put(EXTRAS_VALUE, value);
+        return db.insert(CE_TABLE_EXTRAS, EXTRAS_KEY, values);
+    }
+
+    Map<String, String> findUserExtrasForAccount(Account account) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked();
+        Map<String, String> userExtrasForAccount = new HashMap<>();
+        String[] selectionArgs = {account.name, account.type};
+        try (Cursor cursor = db.query(CE_TABLE_EXTRAS,
+                COLUMNS_EXTRAS_KEY_AND_VALUE,
+                SELECTION_USERDATA_BY_ACCOUNT,
+                selectionArgs,
+                null, null, null)) {
+            while (cursor.moveToNext()) {
+                final String tmpkey = cursor.getString(0);
+                final String value = cursor.getString(1);
+                userExtrasForAccount.put(tmpkey, value);
+            }
+        }
+        return userExtrasForAccount;
+    }
+
+    long findCeAccountId(Account account) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked();
+        String[] columns = { ACCOUNTS_ID };
+        String selection = "name=? AND type=?";
+        String[] selectionArgs = {account.name, account.type};
+        try (Cursor cursor = db.query(CE_TABLE_ACCOUNTS, columns, selection, selectionArgs,
+                null, null, null)) {
+            if (cursor.moveToNext()) {
+                return cursor.getLong(0);
+            }
+            return -1;
+        }
+    }
+
+    String findAccountPasswordByNameAndType(String name, String type) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked();
+        String selection = ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?";
+        String[] selectionArgs = {name, type};
+        String[] columns = {ACCOUNTS_PASSWORD};
+        try (Cursor cursor = db.query(CE_TABLE_ACCOUNTS, columns, selection, selectionArgs,
+                null, null, null)) {
+            if (cursor.moveToNext()) {
+                return cursor.getString(0);
+            }
+            return null;
+        }
+    }
+
+    long insertCeAccount(Account account, String password) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabaseUserIsUnlocked();
+        ContentValues values = new ContentValues();
+        values.put(ACCOUNTS_NAME, account.name);
+        values.put(ACCOUNTS_TYPE, account.type);
+        values.put(ACCOUNTS_PASSWORD, password);
+        return db.insert(
+                CE_TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
+    }
+
+
     static class DeDatabaseHelper extends SQLiteOpenHelper {
 
         private final int mUserId;
@@ -504,7 +522,7 @@
             createGrantsTable(db);
             createSharedAccountsTable(db);
             createAccountsDeletionTrigger(db);
-            DebugDbHelper.createDebugTable(db);
+            createDebugTable(db);
         }
 
         private void createSharedAccountsTable(SQLiteDatabase db) {
@@ -533,6 +551,18 @@
                     +   "," + GRANTS_GRANTEE_UID + "))");
         }
 
+        static void createDebugTable(SQLiteDatabase db) {
+            db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
+                    + ACCOUNTS_ID + " INTEGER,"
+                    + DEBUG_TABLE_ACTION_TYPE + " TEXT NOT NULL, "
+                    + DEBUG_TABLE_TIMESTAMP + " DATETIME,"
+                    + DEBUG_TABLE_CALLER_UID + " INTEGER NOT NULL,"
+                    + DEBUG_TABLE_TABLE_NAME + " TEXT NOT NULL,"
+                    + DEBUG_TABLE_KEY + " INTEGER PRIMARY KEY)");
+            db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " ("
+                    + DEBUG_TABLE_TIMESTAMP + ")");
+        }
+
         @Override
         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
             Log.i(TAG, "upgrade from version " + oldVersion + " to version " + newVersion);
@@ -542,17 +572,6 @@
             }
         }
 
-        public void attachCeDatabase(File ceDbFile) {
-            SQLiteDatabase db = getWritableDatabase();
-            db.execSQL("ATTACH DATABASE '" +  ceDbFile.getPath()+ "' AS ceDb");
-            mCeAttached = true;
-        }
-
-        public boolean isCeDatabaseAttached() {
-            return mCeAttached;
-        }
-
-
         public SQLiteDatabase getReadableDatabaseUserIsUnlocked() {
             if(!mCeAttached) {
                 Log.wtf(TAG, "getReadableDatabaseUserIsUnlocked called while user " + mUserId
@@ -616,343 +635,308 @@
 
             db.execSQL("DETACH DATABASE preNDb");
         }
+    }
 
-        static boolean deleteAccount(SQLiteDatabase db, long accountId) {
-            return db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0;
-        }
+    boolean deleteDeAccount(long accountId) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        return db.delete(TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0;
+    }
 
-        static long insertSharedAccount(SQLiteDatabase db, Account account) {
-            ContentValues values = new ContentValues();
-            values.put(ACCOUNTS_NAME, account.name);
-            values.put(ACCOUNTS_TYPE, account.type);
-            return db.insert(
-                    TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
-        }
+    long insertSharedAccount(Account account) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        values.put(ACCOUNTS_NAME, account.name);
+        values.put(ACCOUNTS_TYPE, account.type);
+        return db.insert(
+                TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME, values);
+    }
 
-        static boolean deleteSharedAccount(SQLiteDatabase db, Account account) {
-            return db
-                    .delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
-                            new String[]{account.name, account.type}) > 0;
-        }
+    boolean deleteSharedAccount(Account account) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        return db.delete(TABLE_SHARED_ACCOUNTS, ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
+                new String[]{account.name, account.type}) > 0;
+    }
 
-        static int renameSharedAccount(SQLiteDatabase db, Account account, String newName) {
-            final ContentValues values = new ContentValues();
-            values.put(ACCOUNTS_NAME, newName);
-            return db.update(TABLE_SHARED_ACCOUNTS,
-                    values,
-                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
-                            + "=?",
-                    new String[]{account.name, account.type});
-        }
+    int renameSharedAccount(Account account, String newName) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        final ContentValues values = new ContentValues();
+        values.put(ACCOUNTS_NAME, newName);
+        return db.update(TABLE_SHARED_ACCOUNTS,
+                values,
+                ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
+                new String[] {account.name, account.type});
+    }
 
-        static List<Account> getSharedAccounts(SQLiteDatabase db) {
-            ArrayList<Account> accountList = new ArrayList<>();
-            Cursor cursor = null;
-            try {
-                cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{
-                                ACCOUNTS_NAME, ACCOUNTS_TYPE},
-                        null, null, null, null, null);
-                if (cursor != null && cursor.moveToFirst()) {
-                    int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
-                    int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
-                    do {
-                        accountList.add(new Account(cursor.getString(nameIndex),
-                                cursor.getString(typeIndex)));
-                    } while (cursor.moveToNext());
-                }
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
-                }
-            }
-            return accountList;
-        }
-
-        static long findSharedAccountId(SQLiteDatabase db, Account account) {
-            Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{
-                            ACCOUNTS_ID},
-                    "name=? AND type=?", new String[]{account.name, account.type}, null, null,
-                    null);
-            try {
-                if (cursor.moveToNext()) {
-                    return cursor.getLong(0);
-                }
-                return -1;
-            } finally {
-                cursor.close();
-            }
-        }
-
-        static long findAccountLastAuthenticatedTime(SQLiteDatabase db, Account account) {
-            return DatabaseUtils.longForQuery(
-                    db,
-                    "SELECT " + AccountsDb.ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
-                            + " FROM " +
-                            TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
-                            + ACCOUNTS_TYPE + "=?",
-                    new String[] {account.name, account.type});
-        }
-
-        static boolean updateAccountLastAuthenticatedTime(SQLiteDatabase db, Account account) {
-            final ContentValues values = new ContentValues();
-            values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
-            int rowCount = db.update(
-                    TABLE_ACCOUNTS,
-                    values,
-                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
-                    new String[] {
-                            account.name, account.type
-                    });
-            return rowCount > 0;
-        }
-
-
-        static void dumpAccountsTable(SQLiteDatabase db, PrintWriter pw) {
-            Cursor cursor = db.query(
-                    TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
-                    null, null, ACCOUNTS_TYPE, null, null);
-            try {
-                while (cursor.moveToNext()) {
-                    // print type,count
-                    pw.println(cursor.getString(0) + "," + cursor.getString(1));
-                }
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
-                }
-            }
-        }
-
-        static long findAccountId(SQLiteDatabase db, Account account) {
-            Cursor cursor = db.query(
-                    TABLE_ACCOUNTS, new String[]{ACCOUNTS_ID},
-                    "name=? AND type=?", new String[]{account.name, account.type}, null, null,
-                    null);
-            try {
-                if (cursor.moveToNext()) {
-                    return cursor.getLong(0);
-                }
-                return -1;
-            } finally {
-                cursor.close();
-            }
-        }
-
-        static Map<Long, Account> findAllAccounts(SQLiteDatabase db) {
-            LinkedHashMap<Long, Account> map = new LinkedHashMap<>();
-            Cursor cursor = db.query(TABLE_ACCOUNTS,
-                    new String[]{ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME},
-                    null, null, null, null, ACCOUNTS_ID);
-            try {
-                while (cursor.moveToNext()) {
-                    final long accountId = cursor.getLong(0);
-                    final String accountType = cursor.getString(1);
-                    final String accountName = cursor.getString(2);
-
-                    final Account account = new Account(accountName, accountType);
-                    map.put(accountId, account);
-                }
-            } finally {
-                cursor.close();
-            }
-            return map;
-        }
-
-        static String findAccountPreviousName(SQLiteDatabase db, Account account) {
-            Cursor cursor = db.query(
-                    TABLE_ACCOUNTS,
-                    new String[]{ACCOUNTS_PREVIOUS_NAME},
-                    ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE
-                            + "=?",
-                    new String[]{account.name, account.type},
-                    null,
-                    null,
-                    null);
-            try {
-                if (cursor.moveToNext()) {
-                    return cursor.getString(0);
-                }
-            } finally {
-                cursor.close();
-            }
-            return null;
-        }
-
-        static long insertAccount(SQLiteDatabase db, Account account, long accountId) {
-            ContentValues values = new ContentValues();
-            values.put(ACCOUNTS_ID, accountId);
-            values.put(ACCOUNTS_NAME, account.name);
-            values.put(ACCOUNTS_TYPE, account.type);
-            values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
-            return db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
-        }
-
-        static boolean renameAccount(SQLiteDatabase db, long accountId, String newName,
-                String previousName) {
-            final ContentValues values = new ContentValues();
-            values.put(ACCOUNTS_NAME, newName);
-            values.put(ACCOUNTS_PREVIOUS_NAME, previousName);
-            final String[] argsAccountId = {String.valueOf(accountId)};
-            return db.update(
-                    TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId) > 0;
-        }
-
-        static boolean deleteGrantsByAccountIdAuthTokenTypeAndUid(SQLiteDatabase db, long accountId,
-                String authTokenType, long uid) {
-            return db.delete(TABLE_GRANTS,
-                    GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
-                            + GRANTS_GRANTEE_UID + "=?",
-                    new String[]{String.valueOf(accountId), authTokenType, String.valueOf(uid)})
-                    > 0;
-        }
-
-        static List<Integer> findAllUidGrants(SQLiteDatabase db) {
-            List<Integer> result = new ArrayList<>();
-            final Cursor cursor = db.query(TABLE_GRANTS,
-                    new String[]{GRANTS_GRANTEE_UID},
-                    null, null, GRANTS_GRANTEE_UID, null, null);
-            try {
-                while (cursor.moveToNext()) {
-                    final int uid = cursor.getInt(0);
-                    result.add(uid);
-                }
-            } finally {
-                cursor.close();
-            }
-            return result;
-        }
-
-        static long findMatchingGrantsCount(SQLiteDatabase db,
-                int uid, String authTokenType, Account account) {
-            String[] args = {String.valueOf(uid), authTokenType,
-                    account.name, account.type};
-            return DatabaseUtils
-                    .longForQuery(db, COUNT_OF_MATCHING_GRANTS, args);
-        }
-
-        static long findMatchingGrantsCountAnyToken(SQLiteDatabase db,
-                int uid, Account account) {
-            String[] args = {String.valueOf(uid), account.name, account.type};
-            return DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS_ANY_TOKEN, args);
-        }
-
-        static long insertGrant(SQLiteDatabase db, long accountId, String authTokenType, int uid) {
-            ContentValues values = new ContentValues();
-            values.put(GRANTS_ACCOUNTS_ID, accountId);
-            values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
-            values.put(GRANTS_GRANTEE_UID, uid);
-            return db.insert(
-                    TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
-        }
-
-        static boolean deleteGrantsByUid(SQLiteDatabase db, int uid) {
-            return db.delete(
-                    TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
-                    new String[]{Integer.toString(uid)}) > 0;
-        }
-
-        static long insertMetaAuthTypeAndUid(SQLiteDatabase db, String authenticatorType, int uid) {
-            ContentValues values = new ContentValues();
-            values.put(META_KEY,
-                    META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType);
-            values.put(META_VALUE, uid);
-            return db.insert(TABLE_META, null, values);
-        }
-
-        static long insertOrReplaceMetaAuthTypeAndUid(SQLiteDatabase db, String authenticatorType,
-                int uid) {
-            ContentValues values = new ContentValues();
-            values.put(META_KEY,
-                    META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType);
-            values.put(META_VALUE, uid);
-            return db.insertWithOnConflict(TABLE_META, null, values,
-                    SQLiteDatabase.CONFLICT_REPLACE);
-        }
-
-        static Map<String, Integer> findMetaAuthUid(SQLiteDatabase db) {
-            Cursor metaCursor = db.query(
-                    TABLE_META,
-                    new String[]{META_KEY, META_VALUE},
-                    SELECTION_META_BY_AUTHENTICATOR_TYPE,
-                    new String[]{META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + "%"},
-                    null /* groupBy */,
-                    null /* having */,
-                    META_KEY);
-            Map<String, Integer> map = new LinkedHashMap<>();
-            try {
-                while (metaCursor.moveToNext()) {
-                    String type = TextUtils
-                            .split(metaCursor.getString(0), META_KEY_DELIMITER)[1];
-                    String uidStr = metaCursor.getString(1);
-                    if (TextUtils.isEmpty(type) || TextUtils.isEmpty(uidStr)) {
-                        // Should never happen.
-                        Slog.e(TAG, "Auth type empty: " + TextUtils.isEmpty(type)
-                                + ", uid empty: " + TextUtils.isEmpty(uidStr));
-                        continue;
-                    }
-                    int uid = Integer.parseInt(metaCursor.getString(1));
-                    map.put(type, uid);
-                }
-            } finally {
-                metaCursor.close();
-            }
-            return map;
-        }
-
-        static boolean deleteMetaByAuthTypeAndUid(SQLiteDatabase db, String type, int uid) {
-            return db.delete(
-                    TABLE_META,
-                    META_KEY + "=? AND " + META_VALUE + "=?",
-                    new String[]{
-                            META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + type,
-                            String.valueOf(uid)}
-            ) > 0;
-        }
-
-        static List<Pair<String, Integer>> findAllAccountGrants(SQLiteDatabase db) {
-            try (Cursor cursor = db.rawQuery(ACCOUNT_ACCESS_GRANTS, null)) {
-                if (cursor == null || !cursor.moveToFirst()) {
-                    return Collections.emptyList();
-                }
-                List<Pair<String, Integer>> results = new ArrayList<>();
+    List<Account> getSharedAccounts() {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        ArrayList<Account> accountList = new ArrayList<>();
+        Cursor cursor = null;
+        try {
+            cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[] {ACCOUNTS_NAME, ACCOUNTS_TYPE},
+                    null, null, null, null, null);
+            if (cursor != null && cursor.moveToFirst()) {
+                int nameIndex = cursor.getColumnIndex(ACCOUNTS_NAME);
+                int typeIndex = cursor.getColumnIndex(ACCOUNTS_TYPE);
                 do {
-                    final String accountName = cursor.getString(0);
-                    final int uid = cursor.getInt(1);
-                    results.add(Pair.create(accountName, uid));
+                    accountList.add(new Account(cursor.getString(nameIndex),
+                            cursor.getString(typeIndex)));
                 } while (cursor.moveToNext());
-                return results;
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
             }
         }
+        return accountList;
+    }
 
-        static DeDatabaseHelper create(
-                Context context,
-                int userId,
-                File preNDatabaseFile,
-                File deDatabaseFile) {
-            boolean newDbExists = deDatabaseFile.exists();
-            DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId,
-                    deDatabaseFile.getPath());
-            // If the db just created, and there is a legacy db, migrate it
-            if (!newDbExists && preNDatabaseFile.exists()) {
-                // Migrate legacy db to the latest version -  PRE_N_DATABASE_VERSION
-                PreNDatabaseHelper
-                        preNDatabaseHelper = new PreNDatabaseHelper(context, userId,
-                        preNDatabaseFile.getPath());
-                // Open the database to force upgrade if required
-                preNDatabaseHelper.getWritableDatabase();
-                preNDatabaseHelper.close();
-                // Move data without SPII to DE
-                deDatabaseHelper.migratePreNDbToDe(preNDatabaseFile);
+    long findSharedAccountId(Account account) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        Cursor cursor = db.query(TABLE_SHARED_ACCOUNTS, new String[]{
+                        ACCOUNTS_ID},
+                "name=? AND type=?", new String[]{account.name, account.type}, null, null,
+                null);
+        try {
+            if (cursor.moveToNext()) {
+                return cursor.getLong(0);
             }
-            return deDatabaseHelper;
+            return -1;
+        } finally {
+            cursor.close();
         }
     }
 
-    static class PreNDatabaseHelper extends SQLiteOpenHelper {
+    long findAccountLastAuthenticatedTime(Account account) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        return DatabaseUtils.longForQuery(db,
+                "SELECT " + AccountsDb.ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS
+                        + " FROM " + TABLE_ACCOUNTS + " WHERE " + ACCOUNTS_NAME + "=? AND "
+                        + ACCOUNTS_TYPE + "=?",
+                new String[] {account.name, account.type});
+    }
+
+    boolean updateAccountLastAuthenticatedTime(Account account) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        final ContentValues values = new ContentValues();
+        values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
+        int rowCount = db.update(TABLE_ACCOUNTS,
+                values,
+                ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?",
+                new String[] { account.name, account.type });
+        return rowCount > 0;
+    }
+
+    void dumpDeAccountsTable(PrintWriter pw) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        Cursor cursor = db.query(
+                TABLE_ACCOUNTS, ACCOUNT_TYPE_COUNT_PROJECTION,
+                null, null, ACCOUNTS_TYPE, null, null);
+        try {
+            while (cursor.moveToNext()) {
+                // print type,count
+                pw.println(cursor.getString(0) + "," + cursor.getString(1));
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+
+    long findDeAccountId(Account account) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        String[] columns = {ACCOUNTS_ID};
+        String selection = "name=? AND type=?";
+        String[] selectionArgs = {account.name, account.type};
+        try (Cursor cursor = db.query(TABLE_ACCOUNTS, columns, selection, selectionArgs,
+                null, null, null)) {
+            if (cursor.moveToNext()) {
+                return cursor.getLong(0);
+            }
+            return -1;
+        }
+    }
+
+    Map<Long, Account> findAllDeAccounts() {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        LinkedHashMap<Long, Account> map = new LinkedHashMap<>();
+        String[] columns = {ACCOUNTS_ID, ACCOUNTS_TYPE, ACCOUNTS_NAME};
+        try (Cursor cursor = db.query(TABLE_ACCOUNTS, columns,
+                null, null, null, null, ACCOUNTS_ID)) {
+            while (cursor.moveToNext()) {
+                final long accountId = cursor.getLong(0);
+                final String accountType = cursor.getString(1);
+                final String accountName = cursor.getString(2);
+
+                final Account account = new Account(accountName, accountType);
+                map.put(accountId, account);
+            }
+        }
+        return map;
+    }
+
+    String findDeAccountPreviousName(Account account) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        String[] columns = {ACCOUNTS_PREVIOUS_NAME};
+        String selection = ACCOUNTS_NAME + "=? AND " + ACCOUNTS_TYPE + "=?";
+        String[] selectionArgs = {account.name, account.type};
+        try (Cursor cursor = db.query(TABLE_ACCOUNTS, columns, selection, selectionArgs,
+                null, null, null)) {
+            if (cursor.moveToNext()) {
+                return cursor.getString(0);
+            }
+        }
+        return null;
+    }
+
+    long insertDeAccount(Account account, long accountId) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        values.put(ACCOUNTS_ID, accountId);
+        values.put(ACCOUNTS_NAME, account.name);
+        values.put(ACCOUNTS_TYPE, account.type);
+        values.put(ACCOUNTS_LAST_AUTHENTICATE_TIME_EPOCH_MILLIS, System.currentTimeMillis());
+        return db.insert(TABLE_ACCOUNTS, ACCOUNTS_NAME, values);
+    }
+
+    boolean renameDeAccount(long accountId, String newName, String previousName) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        final ContentValues values = new ContentValues();
+        values.put(ACCOUNTS_NAME, newName);
+        values.put(ACCOUNTS_PREVIOUS_NAME, previousName);
+        final String[] argsAccountId = {String.valueOf(accountId)};
+        return db.update(TABLE_ACCOUNTS, values, ACCOUNTS_ID + "=?", argsAccountId) > 0;
+    }
+
+    boolean deleteGrantsByAccountIdAuthTokenTypeAndUid(long accountId,
+            String authTokenType, long uid) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        return db.delete(TABLE_GRANTS,
+                GRANTS_ACCOUNTS_ID + "=? AND " + GRANTS_AUTH_TOKEN_TYPE + "=? AND "
+                        + GRANTS_GRANTEE_UID + "=?",
+                new String[] {String.valueOf(accountId), authTokenType, String.valueOf(uid)}) > 0;
+    }
+
+    List<Integer> findAllUidGrants() {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        List<Integer> result = new ArrayList<>();
+        final Cursor cursor = db.query(TABLE_GRANTS,
+                new String[]{GRANTS_GRANTEE_UID},
+                null, null, GRANTS_GRANTEE_UID, null, null);
+        try {
+            while (cursor.moveToNext()) {
+                final int uid = cursor.getInt(0);
+                result.add(uid);
+            }
+        } finally {
+            cursor.close();
+        }
+        return result;
+    }
+
+    long findMatchingGrantsCount(int uid, String authTokenType, Account account) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        String[] args = {String.valueOf(uid), authTokenType, account.name, account.type};
+        return DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS, args);
+    }
+
+    long findMatchingGrantsCountAnyToken(int uid, Account account) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        String[] args = {String.valueOf(uid), account.name, account.type};
+        return DatabaseUtils.longForQuery(db, COUNT_OF_MATCHING_GRANTS_ANY_TOKEN, args);
+    }
+
+    long insertGrant(long accountId, String authTokenType, int uid) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        values.put(GRANTS_ACCOUNTS_ID, accountId);
+        values.put(GRANTS_AUTH_TOKEN_TYPE, authTokenType);
+        values.put(GRANTS_GRANTEE_UID, uid);
+        return db.insert(TABLE_GRANTS, GRANTS_ACCOUNTS_ID, values);
+    }
+
+    boolean deleteGrantsByUid(int uid) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        return db.delete(TABLE_GRANTS, GRANTS_GRANTEE_UID + "=?",
+                new String[] {Integer.toString(uid)}) > 0;
+    }
+
+    long insertOrReplaceMetaAuthTypeAndUid(String authenticatorType, int uid) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        ContentValues values = new ContentValues();
+        values.put(META_KEY,
+                META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType);
+        values.put(META_VALUE, uid);
+        return db.insertWithOnConflict(TABLE_META, null, values,
+                SQLiteDatabase.CONFLICT_REPLACE);
+    }
+
+    Map<String, Integer> findMetaAuthUid() {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        Cursor metaCursor = db.query(
+                TABLE_META,
+                new String[]{META_KEY, META_VALUE},
+                SELECTION_META_BY_AUTHENTICATOR_TYPE,
+                new String[]{META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + "%"},
+                null /* groupBy */,
+                null /* having */,
+                META_KEY);
+        Map<String, Integer> map = new LinkedHashMap<>();
+        try {
+            while (metaCursor.moveToNext()) {
+                String type = TextUtils
+                        .split(metaCursor.getString(0), META_KEY_DELIMITER)[1];
+                String uidStr = metaCursor.getString(1);
+                if (TextUtils.isEmpty(type) || TextUtils.isEmpty(uidStr)) {
+                    // Should never happen.
+                    Slog.e(TAG, "Auth type empty: " + TextUtils.isEmpty(type)
+                            + ", uid empty: " + TextUtils.isEmpty(uidStr));
+                    continue;
+                }
+                int uid = Integer.parseInt(metaCursor.getString(1));
+                map.put(type, uid);
+            }
+        } finally {
+            metaCursor.close();
+        }
+        return map;
+    }
+
+    boolean deleteMetaByAuthTypeAndUid(String type, int uid) {
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        return db.delete(
+                TABLE_META,
+                META_KEY + "=? AND " + META_VALUE + "=?",
+                new String[]{
+                        META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + type,
+                        String.valueOf(uid)}
+        ) > 0;
+    }
+
+    /**
+     * Returns list of all grants as {@link Pair pairs} of account name and UID.
+     */
+    List<Pair<String, Integer>> findAllAccountGrants() {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        try (Cursor cursor = db.rawQuery(ACCOUNT_ACCESS_GRANTS, null)) {
+            if (cursor == null || !cursor.moveToFirst()) {
+                return Collections.emptyList();
+            }
+            List<Pair<String, Integer>> results = new ArrayList<>();
+            do {
+                final String accountName = cursor.getString(0);
+                final int uid = cursor.getInt(1);
+                results.add(Pair.create(accountName, uid));
+            } while (cursor.moveToNext());
+            return results;
+        }
+    }
+
+    private static class PreNDatabaseHelper extends SQLiteOpenHelper {
         private final Context mContext;
         private final int mUserId;
 
-        public PreNDatabaseHelper(Context context, int userId, String preNDatabaseName) {
+        PreNDatabaseHelper(Context context, int userId, String preNDatabaseName) {
             super(context, preNDatabaseName, null, PRE_N_DATABASE_VERSION);
             mContext = context;
             mUserId = userId;
@@ -982,7 +966,7 @@
         }
 
         private void addDebugTable(SQLiteDatabase db) {
-            DebugDbHelper.createDebugTable(db);
+            DeDatabaseHelper.createDebugTable(db);
         }
 
         private void createAccountsDeletionTrigger(SQLiteDatabase db) {
@@ -1007,10 +991,18 @@
                     +   "," + GRANTS_GRANTEE_UID + "))");
         }
 
+        static long insertMetaAuthTypeAndUid(SQLiteDatabase db, String authenticatorType, int uid) {
+            ContentValues values = new ContentValues();
+            values.put(META_KEY,
+                    META_KEY_FOR_AUTHENTICATOR_UID_FOR_TYPE_PREFIX + authenticatorType);
+            values.put(META_VALUE, uid);
+            return db.insert(TABLE_META, null, values);
+        }
+
         private void populateMetaTableWithAuthTypeAndUID(SQLiteDatabase db,
                 Map<String, Integer> authTypeAndUIDMap) {
             for (Map.Entry<String, Integer> entry : authTypeAndUIDMap.entrySet()) {
-                DeDatabaseHelper.insertMetaAuthTypeAndUid(db, entry.getKey(), entry.getValue());
+                insertMetaAuthTypeAndUid(db, entry.getKey(), entry.getValue());
             }
         }
 
@@ -1078,68 +1070,8 @@
         }
     }
 
-    static class DebugDbHelper{
-        private DebugDbHelper() {
-        }
-
-
-        private static void createDebugTable(SQLiteDatabase db) {
-            db.execSQL("CREATE TABLE " + TABLE_DEBUG + " ( "
-                    + ACCOUNTS_ID + " INTEGER,"
-                    + DEBUG_TABLE_ACTION_TYPE + " TEXT NOT NULL, "
-                    + DEBUG_TABLE_TIMESTAMP + " DATETIME,"
-                    + DEBUG_TABLE_CALLER_UID + " INTEGER NOT NULL,"
-                    + DEBUG_TABLE_TABLE_NAME + " TEXT NOT NULL,"
-                    + DEBUG_TABLE_KEY + " INTEGER PRIMARY KEY)");
-            db.execSQL("CREATE INDEX timestamp_index ON " + TABLE_DEBUG + " ("
-                    + DEBUG_TABLE_TIMESTAMP + ")");
-        }
-
-        static SQLiteStatement compileSqlStatementForLogging(SQLiteDatabase db) {
-            String sql = "INSERT OR REPLACE INTO " + AccountsDb.TABLE_DEBUG
-                    + " VALUES (?,?,?,?,?,?)";
-            return db.compileStatement(sql);
-        }
-
-        static int getDebugTableRowCount(SQLiteDatabase db) {
-            String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + TABLE_DEBUG;
-            return (int) DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
-        }
-
-        /*
-         * Finds the row key where the next insertion should take place. This should
-         * be invoked only if the table has reached its full capacity.
-         */
-        static int getDebugTableInsertionPoint(SQLiteDatabase db) {
-            // This query finds the smallest timestamp value (and if 2 records have
-            // same timestamp, the choose the lower id).
-            String queryCountDebugDbRows = "SELECT " + DEBUG_TABLE_KEY +
-                    " FROM " + TABLE_DEBUG +
-                    " ORDER BY "  + DEBUG_TABLE_TIMESTAMP + "," + DEBUG_TABLE_KEY +
-                    " LIMIT 1";
-            return (int) DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
-        }
-
-        static void dumpDebugTable(SQLiteDatabase db, PrintWriter pw) {
-            Cursor cursor = db.query(TABLE_DEBUG, null,
-                    null, null, null, null, DEBUG_TABLE_TIMESTAMP);
-            pw.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
-            pw.println("Accounts History");
-            try {
-                while (cursor.moveToNext()) {
-                    // print type,count
-                    pw.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
-                            cursor.getString(2) + "," + cursor.getString(3) + ","
-                            + cursor.getString(4) + "," + cursor.getString(5));
-                }
-            } finally {
-                cursor.close();
-            }
-        }
-
-    }
-
-    static List<Account> findCeAccountsNotInDe(SQLiteDatabase db) {
+    List<Account> findCeAccountsNotInDe() {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked();
         // Select accounts from CE that do not exist in DE
         Cursor cursor = db.rawQuery(
                 "SELECT " + ACCOUNTS_NAME + "," + ACCOUNTS_TYPE
@@ -1161,14 +1093,110 @@
         }
     }
 
-    static boolean deleteCeAccount(SQLiteDatabase db, long accountId) {
+    boolean deleteCeAccount(long accountId) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabaseUserIsUnlocked();
         return db.delete(
                 CE_TABLE_ACCOUNTS, ACCOUNTS_ID + "=" + accountId, null) > 0;
     }
 
+    boolean isCeDatabaseAttached() {
+        return mDeDatabase.mCeAttached;
+    }
+
+    void beginTransaction() {
+        mDeDatabase.getWritableDatabase().beginTransaction();
+    }
+
+    void setTransactionSuccessful() {
+        mDeDatabase.getWritableDatabase().setTransactionSuccessful();
+    }
+
+    void endTransaction() {
+        mDeDatabase.getWritableDatabase().endTransaction();
+    }
+
+    void attachCeDatabase(File ceDbFile) {
+        CeDatabaseHelper.create(mContext, mPreNDatabaseFile, ceDbFile);
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        db.execSQL("ATTACH DATABASE '" +  ceDbFile.getPath()+ "' AS ceDb");
+        mDeDatabase.mCeAttached = true;
+    }
+
+    /*
+     * Finds the row key where the next insertion should take place. Returns number of rows
+     * if it is less {@link #MAX_DEBUG_DB_SIZE}, otherwise finds the lowest number available.
+     */
+    int calculateDebugTableInsertionPoint() {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        String queryCountDebugDbRows = "SELECT COUNT(*) FROM " + TABLE_DEBUG;
+        int size = (int) DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
+        if (size < MAX_DEBUG_DB_SIZE) {
+            return size;
+        }
+
+        // This query finds the smallest timestamp value (and if 2 records have
+        // same timestamp, the choose the lower id).
+        queryCountDebugDbRows = "SELECT " + DEBUG_TABLE_KEY +
+                " FROM " + TABLE_DEBUG +
+                " ORDER BY "  + DEBUG_TABLE_TIMESTAMP + "," + DEBUG_TABLE_KEY +
+                " LIMIT 1";
+        return (int) DatabaseUtils.longForQuery(db, queryCountDebugDbRows, null);
+    }
+
+    SQLiteStatement compileSqlStatementForLogging() {
+        // TODO b/31708085 Fix debug logging - it eagerly opens database for write without a need
+        SQLiteDatabase db = mDeDatabase.getWritableDatabase();
+        String sql = "INSERT OR REPLACE INTO " + AccountsDb.TABLE_DEBUG
+                + " VALUES (?,?,?,?,?,?)";
+        return db.compileStatement(sql);
+    }
+
+    void dumpDebugTable(PrintWriter pw) {
+        SQLiteDatabase db = mDeDatabase.getReadableDatabase();
+        Cursor cursor = db.query(TABLE_DEBUG, null,
+                null, null, null, null, DEBUG_TABLE_TIMESTAMP);
+        pw.println("AccountId, Action_Type, timestamp, UID, TableName, Key");
+        pw.println("Accounts History");
+        try {
+            while (cursor.moveToNext()) {
+                // print type,count
+                pw.println(cursor.getString(0) + "," + cursor.getString(1) + "," +
+                        cursor.getString(2) + "," + cursor.getString(3) + ","
+                        + cursor.getString(4) + "," + cursor.getString(5));
+            }
+        } finally {
+            cursor.close();
+        }
+    }
+
+    public void close() {
+        mDeDatabase.close();
+    }
+
     static void deleteDbFileWarnIfFailed(File dbFile) {
         if (!SQLiteDatabase.deleteDatabase(dbFile)) {
             Log.w(TAG, "Database at " + dbFile + " was not deleted successfully");
         }
     }
+
+    public static AccountsDb create(Context context, int userId, File preNDatabaseFile,
+            File deDatabaseFile) {
+        boolean newDbExists = deDatabaseFile.exists();
+        DeDatabaseHelper deDatabaseHelper = new DeDatabaseHelper(context, userId,
+                deDatabaseFile.getPath());
+        // If the db just created, and there is a legacy db, migrate it
+        if (!newDbExists && preNDatabaseFile.exists()) {
+            // Migrate legacy db to the latest version -  PRE_N_DATABASE_VERSION
+            PreNDatabaseHelper
+                    preNDatabaseHelper = new PreNDatabaseHelper(context, userId,
+                    preNDatabaseFile.getPath());
+            // Open the database to force upgrade if required
+            preNDatabaseHelper.getWritableDatabase();
+            preNDatabaseHelper.close();
+            // Move data without SPII to DE
+            deDatabaseHelper.migratePreNDbToDe(preNDatabaseFile);
+        }
+        return new AccountsDb(deDatabaseHelper, context, preNDatabaseFile);
+    }
+
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index e5b611c..c32cac8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -182,6 +182,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
+import android.os.ShellCallback;
 import android.os.StrictMode;
 import android.os.SystemClock;
 import android.os.SystemProperties;
@@ -6126,6 +6127,8 @@
                 ProcessList.INVALID_ADJ, callerWillRestart, true, doit, evenPersistent,
                 packageName == null ? ("stop user " + userId) : ("stop " + packageName));
 
+        didSomething |= mActivityStarter.clearPendingActivityLaunchesLocked(packageName);
+
         if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
                 packageName, null, doit, evenPersistent, userId)) {
             if (!doit) {
@@ -14024,9 +14027,10 @@
 
     @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out,
-            FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+            FileDescriptor err, String[] args, ShellCallback callback,
+            ResultReceiver resultReceiver) {
         (new ActivityManagerShellCommand(this, false)).exec(
-                this, in, out, err, args, resultReceiver);
+                this, in, out, err, args, callback, resultReceiver);
     }
 
     @Override
@@ -14249,7 +14253,8 @@
                 // Dumping a single activity?
                 if (!dumpActivity(fd, pw, cmd, args, opti, dumpAll, dumpVisibleStacks)) {
                     ActivityManagerShellCommand shell = new ActivityManagerShellCommand(this, true);
-                    int res = shell.exec(this, null, fd, null, args, new ResultReceiver(null));
+                    int res = shell.exec(this, null, fd, null, args, null,
+                            new ResultReceiver(null));
                     if (res < 0) {
                         pw.println("Bad activity command, or no activities match: " + cmd);
                         pw.println("Use -h for help.");
@@ -20679,8 +20684,11 @@
             final long now = SystemClock.elapsedRealtime();
             Long lastReported = userState.mProviderLastReportedFg.get(authority);
             if (lastReported == null || lastReported < now - 60 * 1000L) {
-                mUsageStatsService.reportContentProviderUsage(
-                        authority, providerPkgName, app.userId);
+                if (mSystemReady) {
+                    // Cannot touch the user stats if not system ready
+                    mUsageStatsService.reportContentProviderUsage(
+                            authority, providerPkgName, app.userId);
+                }
                 userState.mProviderLastReportedFg.put(authority, now);
             }
         }
@@ -21697,6 +21705,13 @@
         return mUserController.getCurrentUser();
     }
 
+    String getStartedUserState(int userId) {
+        synchronized (this) {
+            final UserState userState = mUserController.getStartedUserStateLocked(userId);
+            return UserState.stateToString(userState.state);
+        }
+    }
+
     @Override
     public boolean isUserRunning(int userId, int flags) {
         if (!mUserController.isSameProfileGroup(userId, UserHandle.getCallingUserId())
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index adf6d36..7142d3f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -17,26 +17,57 @@
 package com.android.server.am;
 
 import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.AppGlobals;
 import android.app.IActivityManager;
+import android.app.ProfilerInfo;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.ShellCommand;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.util.DebugUtils;
 
 import java.io.PrintWriter;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 
 class ActivityManagerShellCommand extends ShellCommand {
+    public static final String NO_CLASS_ERROR_CODE = "Error type 3";
+
     // IPC interface to activity manager -- don't need to do additional security checks.
     final IActivityManager mInterface;
 
     // Internal service impl -- must perform security checks before touching.
     final ActivityManagerService mInternal;
 
+    // Convenience for interacting with package manager.
+    final IPackageManager mPm;
+
+    private int mStartFlags = 0;
+    private boolean mWaitOption = false;
+    private boolean mStopOption = false;
+
+    private int mRepeat = 0;
+    private int mUserId;
+    private String mReceiverPermission;
+
+    private String mProfileFile;
+    private int mSamplingInterval;
+    private boolean mAutoStop;
+    private int mStackId;
+
     final boolean mDumping;
 
     ActivityManagerShellCommand(ActivityManagerService service, boolean dumping) {
         mInterface = service;
         mInternal = service;
+        mPm = AppGlobals.getPackageManager();
         mDumping = dumping;
     }
 
@@ -48,6 +79,15 @@
         PrintWriter pw = getOutPrintWriter();
         try {
             switch (cmd) {
+                case "start":
+                case "start-activity":
+                    return runStartActivity(pw);
+                case "startservice":
+                case "start-service":
+                    return 1; //runStartService(pw);
+                case "stopservice":
+                case "stop-service":
+                    return 1; //runStopService(pw);
                 case "force-stop":
                     return runForceStop(pw);
                 case "kill":
@@ -66,6 +106,8 @@
                     return runLenientBackgroundCheck(pw);
                 case "get-uid-state":
                     return getUidState(pw);
+                case "get-started-user-state":
+                    return getStartedUserState(pw);
                 default:
                     return handleDefaultCommands(cmd);
             }
@@ -75,6 +117,241 @@
         return -1;
     }
 
+    private Intent makeIntent(int defUser) throws URISyntaxException {
+        mStartFlags = 0;
+        mWaitOption = false;
+        mStopOption = false;
+        mRepeat = 0;
+        mProfileFile = null;
+        mSamplingInterval = 0;
+        mAutoStop = false;
+        mUserId = defUser;
+        mStackId = INVALID_STACK_ID;
+
+        return Intent.parseCommandArgs(this, new Intent.CommandOptionHandler() {
+            @Override
+            public boolean handleOption(String opt, ShellCommand cmd) {
+                if (opt.equals("-D")) {
+                    mStartFlags |= ActivityManager.START_FLAG_DEBUG;
+                } else if (opt.equals("-N")) {
+                    mStartFlags |= ActivityManager.START_FLAG_NATIVE_DEBUGGING;
+                } else if (opt.equals("-W")) {
+                    mWaitOption = true;
+                } else if (opt.equals("-P")) {
+                    mProfileFile = getNextArgRequired();
+                    mAutoStop = true;
+                } else if (opt.equals("--start-profiler")) {
+                    mProfileFile = getNextArgRequired();
+                    mAutoStop = false;
+                } else if (opt.equals("--sampling")) {
+                    mSamplingInterval = Integer.parseInt(getNextArgRequired());
+                } else if (opt.equals("-R")) {
+                    mRepeat = Integer.parseInt(getNextArgRequired());
+                } else if (opt.equals("-S")) {
+                    mStopOption = true;
+                } else if (opt.equals("--track-allocation")) {
+                    mStartFlags |= ActivityManager.START_FLAG_TRACK_ALLOCATION;
+                } else if (opt.equals("--user")) {
+                    mUserId = UserHandle.parseUserArg(getNextArgRequired());
+                } else if (opt.equals("--receiver-permission")) {
+                    mReceiverPermission = getNextArgRequired();
+                } else if (opt.equals("--stack")) {
+                    mStackId = Integer.parseInt(getNextArgRequired());
+                } else {
+                    return false;
+                }
+                return true;
+            }
+        });
+    }
+
+    ParcelFileDescriptor openOutputFile(String path) {
+        try {
+            ParcelFileDescriptor pfd = getShellCallback().openOutputFile(path,
+                    "u:r:system_server:s0");
+            if (pfd != null) {
+                return pfd;
+            }
+        } catch (RuntimeException e) {
+            getErrPrintWriter().println("Failure opening file: " + e.getMessage());
+        }
+        getErrPrintWriter().println("Error: Unable to open file: " + path);
+        getErrPrintWriter().println("Consider using a file under /data/local/tmp/");
+        return null;
+    }
+
+    int runStartActivity(PrintWriter pw) throws RemoteException {
+        Intent intent;
+        try {
+            intent = makeIntent(UserHandle.USER_CURRENT);
+        } catch (URISyntaxException e) {
+            throw new RuntimeException(e.getMessage(), e);
+        }
+
+        if (mUserId == UserHandle.USER_ALL) {
+            getErrPrintWriter().println("Error: Can't start service with user 'all'");
+            return 1;
+        }
+
+        String mimeType = intent.getType();
+        if (mimeType == null && intent.getData() != null
+                && "content".equals(intent.getData().getScheme())) {
+            mimeType = mInterface.getProviderMimeType(intent.getData(), mUserId);
+        }
+
+        do {
+            if (mStopOption) {
+                String packageName;
+                if (intent.getComponent() != null) {
+                    packageName = intent.getComponent().getPackageName();
+                } else {
+                    List<ResolveInfo> activities = mPm.queryIntentActivities(intent, mimeType, 0,
+                            mUserId).getList();
+                    if (activities == null || activities.size() <= 0) {
+                        getErrPrintWriter().println("Error: Intent does not match any activities: "
+                                + intent);
+                        return 1;
+                    } else if (activities.size() > 1) {
+                        getErrPrintWriter().println(
+                                "Error: Intent matches multiple activities; can't stop: "
+                                + intent);
+                        return 1;
+                    }
+                    packageName = activities.get(0).activityInfo.packageName;
+                }
+                pw.println("Stopping: " + packageName);
+                mInterface.forceStopPackage(packageName, mUserId);
+                try {
+                    Thread.sleep(250);
+                } catch (InterruptedException e) {
+                }
+            }
+
+            ProfilerInfo profilerInfo = null;
+
+            if (mProfileFile != null) {
+                ParcelFileDescriptor fd = openOutputFile(mProfileFile);
+                if (fd == null) {
+                    return 1;
+                }
+                profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop);
+            }
+
+            pw.println("Starting: " + intent);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+            IActivityManager.WaitResult result = null;
+            int res;
+            final long startTime = SystemClock.uptimeMillis();
+            ActivityOptions options = null;
+            if (mStackId != INVALID_STACK_ID) {
+                options = ActivityOptions.makeBasic();
+                options.setLaunchStackId(mStackId);
+            }
+            if (mWaitOption) {
+                result = mInterface.startActivityAndWait(null, null, intent, mimeType,
+                        null, null, 0, mStartFlags, profilerInfo,
+                        options != null ? options.toBundle() : null, mUserId);
+                res = result.result;
+            } else {
+                res = mInterface.startActivityAsUser(null, null, intent, mimeType,
+                        null, null, 0, mStartFlags, profilerInfo,
+                        options != null ? options.toBundle() : null, mUserId);
+            }
+            final long endTime = SystemClock.uptimeMillis();
+            PrintWriter out = mWaitOption ? pw : getErrPrintWriter();
+            boolean launched = false;
+            switch (res) {
+                case ActivityManager.START_SUCCESS:
+                    launched = true;
+                    break;
+                case ActivityManager.START_SWITCHES_CANCELED:
+                    launched = true;
+                    out.println(
+                            "Warning: Activity not started because the "
+                                    + " current activity is being kept for the user.");
+                    break;
+                case ActivityManager.START_DELIVERED_TO_TOP:
+                    launched = true;
+                    out.println(
+                            "Warning: Activity not started, intent has "
+                                    + "been delivered to currently running "
+                                    + "top-most instance.");
+                    break;
+                case ActivityManager.START_RETURN_INTENT_TO_CALLER:
+                    launched = true;
+                    out.println(
+                            "Warning: Activity not started because intent "
+                                    + "should be handled by the caller");
+                    break;
+                case ActivityManager.START_TASK_TO_FRONT:
+                    launched = true;
+                    out.println(
+                            "Warning: Activity not started, its current "
+                                    + "task has been brought to the front");
+                    break;
+                case ActivityManager.START_INTENT_NOT_RESOLVED:
+                    out.println(
+                            "Error: Activity not started, unable to "
+                                    + "resolve " + intent.toString());
+                    break;
+                case ActivityManager.START_CLASS_NOT_FOUND:
+                    out.println(NO_CLASS_ERROR_CODE);
+                    out.println("Error: Activity class " +
+                            intent.getComponent().toShortString()
+                            + " does not exist.");
+                    break;
+                case ActivityManager.START_FORWARD_AND_REQUEST_CONFLICT:
+                    out.println(
+                            "Error: Activity not started, you requested to "
+                                    + "both forward and receive its result");
+                    break;
+                case ActivityManager.START_PERMISSION_DENIED:
+                    out.println(
+                            "Error: Activity not started, you do not "
+                                    + "have permission to access it.");
+                    break;
+                case ActivityManager.START_NOT_VOICE_COMPATIBLE:
+                    out.println(
+                            "Error: Activity not started, voice control not allowed for: "
+                                    + intent);
+                    break;
+                case ActivityManager.START_NOT_CURRENT_USER_ACTIVITY:
+                    out.println(
+                            "Error: Not allowed to start background user activity"
+                                    + " that shouldn't be displayed for all users.");
+                    break;
+                default:
+                    out.println(
+                            "Error: Activity not started, unknown error code " + res);
+                    break;
+            }
+            if (mWaitOption && launched) {
+                if (result == null) {
+                    result = new IActivityManager.WaitResult();
+                    result.who = intent.getComponent();
+                }
+                pw.println("Status: " + (result.timeout ? "timeout" : "ok"));
+                if (result.who != null) {
+                    pw.println("Activity: " + result.who.flattenToShortString());
+                }
+                if (result.thisTime >= 0) {
+                    pw.println("ThisTime: " + result.thisTime);
+                }
+                if (result.totalTime >= 0) {
+                    pw.println("TotalTime: " + result.totalTime);
+                }
+                pw.println("WaitTime: " + (endTime-startTime));
+                pw.println("Complete");
+            }
+            mRepeat--;
+            if (mRepeat > 0) {
+                mInterface.unhandledBack();
+            }
+        } while (mRepeat > 0);
+        return 0;
+    }
+
     int runIsUserStopped(PrintWriter pw) {
         int userId = UserHandle.parseUserArg(getNextArgRequired());
         boolean stopped = mInternal.isUserStopped(userId);
@@ -183,6 +460,18 @@
         return 0;
     }
 
+    int getStartedUserState(PrintWriter pw) throws RemoteException {
+        mInternal.enforceCallingPermission(android.Manifest.permission.DUMP,
+                "getStartedUserState()");
+        final int userId = Integer.parseInt(getNextArgRequired());
+        try {
+            pw.println(mInternal.getStartedUserState(userId));
+        } catch (NullPointerException e) {
+            pw.println("User is not started: " + userId);
+        }
+        return 0;
+    }
+
     @Override
     public void onHelp() {
         PrintWriter pw = getOutPrintWriter();
@@ -223,6 +512,24 @@
             pw.println("Activity manager (activity) commands:");
             pw.println("  help");
             pw.println("    Print this help text.");
+            pw.println("  start-activity [-D] [-N] [-W] [-P <FILE>] [--start-profiler <FILE>]");
+            pw.println("          [--sampling INTERVAL] [-R COUNT] [-S]");
+            pw.println("          [--track-allocation] [--user <USER_ID> | current] <INTENT>");
+            pw.println("    Start an Activity.  Options are:");
+            pw.println("    -D: enable debugging");
+            pw.println("    -N: enable native debugging");
+            pw.println("    -W: wait for launch to complete");
+            pw.println("    --start-profiler <FILE>: start profiler and send results to <FILE>");
+            pw.println("    --sampling INTERVAL: use sample profiling with INTERVAL microseconds");
+            pw.println("        between samples (use with --start-profiler)");
+            pw.println("    -P <FILE>: like above, but profiling stops when app goes idle");
+            pw.println("    -R: repeat the activity launch <COUNT> times.  Prior to each repeat,");
+            pw.println("        the top activity will be finished.");
+            pw.println("    -S: force stop the target app before starting the activity");
+            pw.println("    --track-allocation: enable tracking of object allocations");
+            pw.println("    --user <USER_ID> | current: Specify which user to run as; if not");
+            pw.println("        specified then run as the current user.");
+            pw.println("    --stack <STACK_ID>: Specify into which stack should the activity be put.");
             pw.println("  force-stop [--user <USER_ID> | all | current] <PACKAGE>");
             pw.println("    Completely stop the given application package.");
             pw.println("  kill [--user <USER_ID> | all | current] <PACKAGE>");
@@ -241,6 +548,10 @@
             pw.println("    Optionally controls lenient background check mode, returns current mode.");
             pw.println("  get-uid-state <UID>");
             pw.println("    Gets the process state of an app given its <UID>.");
+            pw.println("  get-started-user-state <USER_ID>");
+            pw.println("    Gets the current state of the given started user.");
+            pw.println();
+            Intent.printIntentArgsHelp(pw, "");
         }
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 7708b02..d820627 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1414,7 +1414,12 @@
      * this function updates the rest of our state to match that fact.
      */
     private void completeResumeLocked(ActivityRecord next) {
+        boolean wasVisible = next.visible;
         next.visible = true;
+        if (!wasVisible) {
+            // Visibility has changed, so take a note of it so we call the TaskStackChangedListener
+            mStackSupervisor.mAppVisibilitiesChangedSinceLastPause = true;
+        }
         next.idle = false;
         next.results = null;
         next.newIntents = null;
@@ -5029,7 +5034,7 @@
         if (top >= 0) {
             final ArrayList<ActivityRecord> activities = mTaskHistory.get(top).mActivities;
             int activityTop = activities.size() - 1;
-            if (activityTop > 0) {
+            if (activityTop >= 0) {
                 finishActivityLocked(activities.get(activityTop), Activity.RESULT_CANCELED, null,
                         "unhandled-back", true);
             }
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 5feaf1f..028c6ac 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -2091,4 +2091,18 @@
         return (flags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&
                 (flags & Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0;
     }
+
+    boolean clearPendingActivityLaunchesLocked(String packageName) {
+        boolean didSomething = false;
+
+        for (int palNdx = mPendingActivityLaunches.size() - 1; palNdx >= 0; --palNdx) {
+            PendingActivityLaunch pal = mPendingActivityLaunches.get(palNdx);
+            ActivityRecord r = pal.r;
+            if (r != null && r.packageName.equals(packageName)) {
+                mPendingActivityLaunches.remove(palNdx);
+                didSomething = true;
+            }
+        }
+        return didSomething;
+    }
 }
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index ba42d3f..9697855 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -115,7 +115,7 @@
 
     // Amount of time we wait for observers to handle a user switch before
     // giving up on them and unfreezing the screen.
-    static final int USER_SWITCH_TIMEOUT = 2 * 1000;
+    static final int USER_SWITCH_TIMEOUT = 3 * 1000;
 
     private final Object mLock;
     private final Injector mInjector;
@@ -1103,6 +1103,7 @@
                 mCurWaitingUserSwitchCallbacks = curWaitingUserSwitchCallbacks;
             }
             final AtomicInteger waitingCallbacksCount = new AtomicInteger(observerCount);
+            final long dispatchStartedTime = SystemClock.elapsedRealtime();
             for (int i = 0; i < observerCount; i++) {
                 try {
                     // Prepend with unique prefix to guarantee that keys are unique
@@ -1114,6 +1115,11 @@
                         @Override
                         public void sendResult(Bundle data) throws RemoteException {
                             synchronized (mLock) {
+                                long delay = SystemClock.elapsedRealtime() - dispatchStartedTime;
+                                if (delay > USER_SWITCH_TIMEOUT) {
+                                    Slog.wtf(TAG, "User switch timeout: observer "  + name
+                                            + " sent result after " + delay + " ms");
+                                }
                                 // Early return if this session is no longer valid
                                 if (curWaitingUserSwitchCallbacks
                                         != mCurWaitingUserSwitchCallbacks) {
diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java
index ff8014c..48238b6 100644
--- a/services/core/java/com/android/server/am/UserState.java
+++ b/services/core/java/com/android/server/am/UserState.java
@@ -87,7 +87,7 @@
         state = newState;
     }
 
-    private static String stateToString(int state) {
+    static String stateToString(int state) {
         switch (state) {
             case STATE_BOOTING: return "BOOTING";
             case STATE_RUNNING_LOCKED: return "RUNNING_LOCKED";
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 66aa403..5772a57 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -20,7 +20,6 @@
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
-import android.content.BroadcastReceiver;
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.ContentProvider;
@@ -28,7 +27,6 @@
 import android.content.IOnPrimaryClipChangedListener;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -47,23 +45,57 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.server.SystemService;
+
 import java.util.HashSet;
 import java.util.List;
 
 /**
  * Implementation of the clipboard for copy and paste.
  */
-public class ClipboardService extends IClipboard.Stub {
+public class ClipboardService extends SystemService {
 
     private static final String TAG = "ClipboardService";
 
-    private final Context mContext;
     private final IActivityManager mAm;
     private final IUserManager mUm;
     private final PackageManager mPm;
     private final AppOpsManager mAppOps;
     private final IBinder mPermissionOwner;
 
+    private final SparseArray<PerUserClipboard> mClipboards = new SparseArray<>();
+
+    /**
+     * Instantiates the clipboard.
+     */
+    public ClipboardService(Context context) {
+        super(context);
+
+        mAm = ActivityManagerNative.getDefault();
+        mPm = getContext().getPackageManager();
+        mUm = (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
+        mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE);
+        IBinder permOwner = null;
+        try {
+            permOwner = mAm.newUriPermissionOwner("clipboard");
+        } catch (RemoteException e) {
+            Slog.w("clipboard", "AM dead", e);
+        }
+        mPermissionOwner = permOwner;
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.CLIPBOARD_SERVICE, new ClipboardImpl());
+    }
+
+    @Override
+    public void onCleanupUser(int userId) {
+        synchronized (mClipboards) {
+            mClipboards.remove(userId);
+        }
+    }
+
     private class ListenerInfo {
         final int mUid;
         final String mPackageName;
@@ -89,52 +121,141 @@
         }
     }
 
-    private SparseArray<PerUserClipboard> mClipboards = new SparseArray<PerUserClipboard>();
+    private class ClipboardImpl extends IClipboard.Stub {
+        @Override
+        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+                throws RemoteException {
+            try {
+                return super.onTransact(code, data, reply, flags);
+            } catch (RuntimeException e) {
+                if (!(e instanceof SecurityException)) {
+                    Slog.wtf("clipboard", "Exception: ", e);
+                }
+                throw e;
+            }
 
-    /**
-     * Instantiates the clipboard.
-     */
-    public ClipboardService(Context context) {
-        mContext = context;
-        mAm = ActivityManagerNative.getDefault();
-        mPm = context.getPackageManager();
-        mUm = (IUserManager) ServiceManager.getService(Context.USER_SERVICE);
-        mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
-        IBinder permOwner = null;
-        try {
-            permOwner = mAm.newUriPermissionOwner("clipboard");
-        } catch (RemoteException e) {
-            Slog.w("clipboard", "AM dead", e);
         }
-        mPermissionOwner = permOwner;
 
-        // Remove the clipboard if a user is removed
-        IntentFilter userFilter = new IntentFilter();
-        userFilter.addAction(Intent.ACTION_USER_REMOVED);
-        mContext.registerReceiver(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
-                if (Intent.ACTION_USER_REMOVED.equals(action)) {
-                    removeClipboard(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+        @Override
+        public void setPrimaryClip(ClipData clip, String callingPackage) {
+            synchronized (this) {
+                if (clip != null && clip.getItemCount() <= 0) {
+                    throw new IllegalArgumentException("No items");
+                }
+                final int callingUid = Binder.getCallingUid();
+                if (mAppOps.noteOp(AppOpsManager.OP_WRITE_CLIPBOARD, callingUid,
+                        callingPackage) != AppOpsManager.MODE_ALLOWED) {
+                    return;
+                }
+                checkDataOwnerLocked(clip, callingUid);
+                final int userId = UserHandle.getUserId(callingUid);
+                PerUserClipboard clipboard = getClipboard(userId);
+                revokeUris(clipboard);
+                setPrimaryClipInternal(clipboard, clip);
+                List<UserInfo> related = getRelatedProfiles(userId);
+                if (related != null) {
+                    int size = related.size();
+                    if (size > 1) { // Related profiles list include the current profile.
+                        boolean canCopy = false;
+                        try {
+                            canCopy = !mUm.getUserRestrictions(userId).getBoolean(
+                                    UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
+                        } catch (RemoteException e) {
+                            Slog.e(TAG, "Remote Exception calling UserManager: " + e);
+                        }
+                        // Copy clip data to related users if allowed. If disallowed, then remove
+                        // primary clip in related users to prevent pasting stale content.
+                        if (!canCopy) {
+                            clip = null;
+                        } else {
+                            // We want to fix the uris of the related user's clip without changing the
+                            // uris of the current user's clip.
+                            // So, copy the ClipData, and then copy all the items, so that nothing
+                            // is shared in memmory.
+                            clip = new ClipData(clip);
+                            for (int i = clip.getItemCount() - 1; i >= 0; i--) {
+                                clip.setItemAt(i, new ClipData.Item(clip.getItemAt(i)));
+                            }
+                            clip.fixUrisLight(userId);
+                        }
+                        for (int i = 0; i < size; i++) {
+                            int id = related.get(i).id;
+                            if (id != userId) {
+                                setPrimaryClipInternal(getClipboard(id), clip);
+                            }
+                        }
+                    }
                 }
             }
-        }, userFilter);
-    }
-
-    @Override
-    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
-            throws RemoteException {
-        try {
-            return super.onTransact(code, data, reply, flags);
-        } catch (RuntimeException e) {
-            if (!(e instanceof SecurityException)) {
-                Slog.wtf("clipboard", "Exception: ", e);
-            }
-            throw e;
         }
-        
-    }
+
+        @Override
+        public ClipData getPrimaryClip(String pkg) {
+            synchronized (this) {
+                if (mAppOps.noteOp(AppOpsManager.OP_READ_CLIPBOARD, Binder.getCallingUid(),
+                        pkg) != AppOpsManager.MODE_ALLOWED) {
+                    return null;
+                }
+                addActiveOwnerLocked(Binder.getCallingUid(), pkg);
+                return getClipboard().primaryClip;
+            }
+        }
+
+        @Override
+        public ClipDescription getPrimaryClipDescription(String callingPackage) {
+            synchronized (this) {
+                if (mAppOps.checkOp(AppOpsManager.OP_READ_CLIPBOARD, Binder.getCallingUid(),
+                        callingPackage) != AppOpsManager.MODE_ALLOWED) {
+                    return null;
+                }
+                PerUserClipboard clipboard = getClipboard();
+                return clipboard.primaryClip != null ? clipboard.primaryClip.getDescription() : null;
+            }
+        }
+
+        @Override
+        public boolean hasPrimaryClip(String callingPackage) {
+            synchronized (this) {
+                if (mAppOps.checkOp(AppOpsManager.OP_READ_CLIPBOARD, Binder.getCallingUid(),
+                        callingPackage) != AppOpsManager.MODE_ALLOWED) {
+                    return false;
+                }
+                return getClipboard().primaryClip != null;
+            }
+        }
+
+        @Override
+        public void addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener,
+                String callingPackage) {
+            synchronized (this) {
+                getClipboard().primaryClipListeners.register(listener,
+                        new ListenerInfo(Binder.getCallingUid(), callingPackage));
+            }
+        }
+
+        @Override
+        public void removePrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) {
+            synchronized (this) {
+                getClipboard().primaryClipListeners.unregister(listener);
+            }
+        }
+
+        @Override
+        public boolean hasClipboardText(String callingPackage) {
+            synchronized (this) {
+                if (mAppOps.checkOp(AppOpsManager.OP_READ_CLIPBOARD, Binder.getCallingUid(),
+                        callingPackage) != AppOpsManager.MODE_ALLOWED) {
+                    return false;
+                }
+                PerUserClipboard clipboard = getClipboard();
+                if (clipboard.primaryClip != null) {
+                    CharSequence text = clipboard.primaryClip.getItemAt(0).getText();
+                    return text != null && text.length() > 0;
+                }
+                return false;
+            }
+        }
+    };
 
     private PerUserClipboard getClipboard() {
         return getClipboard(UserHandle.getCallingUserId());
@@ -151,64 +272,6 @@
         }
     }
 
-    private void removeClipboard(int userId) {
-        synchronized (mClipboards) {
-            mClipboards.remove(userId);
-        }
-    }
-
-    public void setPrimaryClip(ClipData clip, String callingPackage) {
-        synchronized (this) {
-            if (clip != null && clip.getItemCount() <= 0) {
-                throw new IllegalArgumentException("No items");
-            }
-            final int callingUid = Binder.getCallingUid();
-            if (mAppOps.noteOp(AppOpsManager.OP_WRITE_CLIPBOARD, callingUid,
-                    callingPackage) != AppOpsManager.MODE_ALLOWED) {
-                return;
-            }
-            checkDataOwnerLocked(clip, callingUid);
-            final int userId = UserHandle.getUserId(callingUid);
-            PerUserClipboard clipboard = getClipboard(userId);
-            revokeUris(clipboard);
-            setPrimaryClipInternal(clipboard, clip);
-            List<UserInfo> related = getRelatedProfiles(userId);
-            if (related != null) {
-                int size = related.size();
-                if (size > 1) { // Related profiles list include the current profile.
-                    boolean canCopy = false;
-                    try {
-                        canCopy = !mUm.getUserRestrictions(userId).getBoolean(
-                                UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
-                    } catch (RemoteException e) {
-                        Slog.e(TAG, "Remote Exception calling UserManager: " + e);
-                    }
-                    // Copy clip data to related users if allowed. If disallowed, then remove
-                    // primary clip in related users to prevent pasting stale content.
-                    if (!canCopy) {
-                        clip = null;
-                    } else {
-                        // We want to fix the uris of the related user's clip without changing the
-                        // uris of the current user's clip.
-                        // So, copy the ClipData, and then copy all the items, so that nothing
-                        // is shared in memmory.
-                        clip = new ClipData(clip);
-                        for (int i = clip.getItemCount() - 1; i >= 0; i--) {
-                            clip.setItemAt(i, new ClipData.Item(clip.getItemAt(i)));
-                        }
-                        clip.fixUrisLight(userId);
-                    }
-                    for (int i = 0; i < size; i++) {
-                        int id = related.get(i).id;
-                        if (id != userId) {
-                            setPrimaryClipInternal(getClipboard(id), clip);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
     List<UserInfo> getRelatedProfiles(int userId) {
         final List<UserInfo> related;
         final long origId = Binder.clearCallingIdentity();
@@ -251,67 +314,6 @@
             Binder.restoreCallingIdentity(ident);
         }
     }
-    
-    public ClipData getPrimaryClip(String pkg) {
-        synchronized (this) {
-            if (mAppOps.noteOp(AppOpsManager.OP_READ_CLIPBOARD, Binder.getCallingUid(),
-                    pkg) != AppOpsManager.MODE_ALLOWED) {
-                return null;
-            }
-            addActiveOwnerLocked(Binder.getCallingUid(), pkg);
-            return getClipboard().primaryClip;
-        }
-    }
-
-    public ClipDescription getPrimaryClipDescription(String callingPackage) {
-        synchronized (this) {
-            if (mAppOps.checkOp(AppOpsManager.OP_READ_CLIPBOARD, Binder.getCallingUid(),
-                    callingPackage) != AppOpsManager.MODE_ALLOWED) {
-                return null;
-            }
-            PerUserClipboard clipboard = getClipboard();
-            return clipboard.primaryClip != null ? clipboard.primaryClip.getDescription() : null;
-        }
-    }
-
-    public boolean hasPrimaryClip(String callingPackage) {
-        synchronized (this) {
-            if (mAppOps.checkOp(AppOpsManager.OP_READ_CLIPBOARD, Binder.getCallingUid(),
-                    callingPackage) != AppOpsManager.MODE_ALLOWED) {
-                return false;
-            }
-            return getClipboard().primaryClip != null;
-        }
-    }
-
-    public void addPrimaryClipChangedListener(IOnPrimaryClipChangedListener listener,
-            String callingPackage) {
-        synchronized (this) {
-            getClipboard().primaryClipListeners.register(listener,
-                    new ListenerInfo(Binder.getCallingUid(), callingPackage));
-        }
-    }
-
-    public void removePrimaryClipChangedListener(IOnPrimaryClipChangedListener listener) {
-        synchronized (this) {
-            getClipboard().primaryClipListeners.unregister(listener);
-        }
-    }
-
-    public boolean hasClipboardText(String callingPackage) {
-        synchronized (this) {
-            if (mAppOps.checkOp(AppOpsManager.OP_READ_CLIPBOARD, Binder.getCallingUid(),
-                    callingPackage) != AppOpsManager.MODE_ALLOWED) {
-                return false;
-            }
-            PerUserClipboard clipboard = getClipboard();
-            if (clipboard.primaryClip != null) {
-                CharSequence text = clipboard.primaryClip.getItemAt(0).getText();
-                return text != null && text.length() > 0;
-            }
-            return false;
-        }
-    }
 
     private final void checkUriOwnerLocked(Uri uri, int uid) {
         if (!"content".equals(uri.getScheme())) {
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index 5addffb..9523a1c 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -195,6 +195,7 @@
     public void binderDied() {
         Slog.v(TAG, "fingerprintd died");
         mDaemon = null;
+        mCurrentUserId = UserHandle.USER_CURRENT;
         handleError(mHalDeviceId, FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
     }
 
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 74095ac..87f4030 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -18,8 +18,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.os.Build;
 import android.os.LocaleList;
+import android.os.ShellCallback;
 import android.util.Log;
 import android.view.Display;
 import com.android.internal.inputmethod.InputMethodSubtypeHandle;
@@ -91,10 +91,8 @@
 import android.view.Surface;
 import android.view.ViewConfiguration;
 import android.view.WindowManagerPolicy;
-import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
-import android.widget.Toast;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -103,11 +101,8 @@
 import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -1739,8 +1734,9 @@
 
     @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out,
-            FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
-        (new Shell()).exec(this, in, out, err, args, resultReceiver);
+            FileDescriptor err, String[] args, ShellCallback callback,
+            ResultReceiver resultReceiver) {
+        (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
     }
 
     public int onShellCommand(Shell shell, String cmd) {
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 9d93146..fe3a02d 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -44,9 +44,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ServiceInfo;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -62,6 +60,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
+import android.os.ShellCallback;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -1718,9 +1717,9 @@
 
         @Override
         public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
-                String[] args, ResultReceiver resultReceiver) throws RemoteException {
+                String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
                 (new JobSchedulerShellCommand(JobSchedulerService.this)).exec(
-                        this, in, out, err, args, resultReceiver);
+                        this, in, out, err, args, callback, resultReceiver);
         }
     };
 
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 6381aa7..2bccfee 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -123,7 +123,6 @@
 import android.net.NetworkIdentity;
 import android.net.NetworkInfo;
 import android.net.NetworkPolicy;
-import android.net.NetworkPolicyManager;
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkState;
 import android.net.NetworkTemplate;
@@ -144,6 +143,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
+import android.os.ShellCallback;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -2046,25 +2046,30 @@
 
     @Override
     public void setRestrictBackground(boolean restrictBackground) {
-        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
-        final long token = Binder.clearCallingIdentity();
+        Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "setRestrictBackground");
         try {
-            maybeRefreshTrustedTime();
-            synchronized (mUidRulesFirstLock) {
-                if (restrictBackground == mRestrictBackground) {
-                    // Ideally, UI should never allow this scenario...
-                    Slog.w(TAG, "setRestrictBackground: already " + restrictBackground);
-                    return;
+            mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+            final long token = Binder.clearCallingIdentity();
+            try {
+                maybeRefreshTrustedTime();
+                synchronized (mUidRulesFirstLock) {
+                    if (restrictBackground == mRestrictBackground) {
+                        // Ideally, UI should never allow this scenario...
+                        Slog.w(TAG, "setRestrictBackground: already " + restrictBackground);
+                        return;
+                    }
+                    setRestrictBackgroundUL(restrictBackground);
                 }
-                setRestrictBackgroundUL(restrictBackground);
+
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
 
+            mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_CHANGED, restrictBackground ? 1 : 0, 0)
+                    .sendToTarget();
         } finally {
-            Binder.restoreCallingIdentity(token);
+            Trace.traceEnd(Trace.TRACE_TAG_NETWORK);
         }
-
-        mHandler.obtainMessage(MSG_RESTRICT_BACKGROUND_CHANGED, restrictBackground ? 1 : 0, 0)
-                .sendToTarget();
     }
 
     private void setRestrictBackgroundUL(boolean restrictBackground) {
@@ -2386,9 +2391,9 @@
 
     @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
-            String[] args, ResultReceiver resultReceiver) throws RemoteException {
+            String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
         (new NetworkPolicyManagerShellCommand(mContext, this)).exec(
-                this, in, out, err, args, resultReceiver);
+                this, in, out, err, args, callback, resultReceiver);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 689917c..f777aae 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -28,6 +28,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
+import android.os.ShellCallback;
 import android.os.storage.StorageManager;
 import android.util.Log;
 import android.util.Slog;
@@ -107,9 +108,9 @@
 
     @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
-            String[] args, ResultReceiver resultReceiver) throws RemoteException {
+            String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
         (new OtaDexoptShellCommand(this)).exec(
-                this, in, out, err, args, resultReceiver);
+                this, in, out, err, args, callback, resultReceiver);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c76302c..df02b86 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -185,6 +185,7 @@
 import android.os.ResultReceiver;
 import android.os.SELinux;
 import android.os.ServiceManager;
+import android.os.ShellCallback;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.Trace;
@@ -464,6 +465,12 @@
     private static final String PACKAGE_SCHEME = "package";
 
     private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay";
+    /**
+     * If VENDOR_OVERLAY_SKU_PROPERTY is set, search for runtime resource overlay APKs in
+     * VENDOR_OVERLAY_DIR/<value of VENDOR_OVERLAY_SKU_PROPERTY> rather than in
+     * VENDOR_OVERLAY_DIR.
+     */
+    private static final String VENDOR_OVERLAY_SKU_PROPERTY = "ro.boot.vendor.overlay.sku";
 
     private static int DEFAULT_EPHEMERAL_HASH_PREFIX_MASK = 0xFFFFF000;
     private static int DEFAULT_EPHEMERAL_HASH_PREFIX_COUNT = 5;
@@ -2281,8 +2288,14 @@
             // Collect vendor overlay packages.
             // (Do this before scanning any apps.)
             // For security and version matching reason, only consider
-            // overlay packages if they reside in VENDOR_OVERLAY_DIR.
-            File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
+            // overlay packages if they reside in the right directory.
+            File vendorOverlayDir;
+            String overlaySkuDir = SystemProperties.get(VENDOR_OVERLAY_SKU_PROPERTY);
+            if (!overlaySkuDir.isEmpty()) {
+                vendorOverlayDir = new File(VENDOR_OVERLAY_DIR, overlaySkuDir);
+            } else {
+                vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
+            }
             scanDirTracedLI(vendorOverlayDir, mDefParseFlags
                     | PackageParser.PARSE_IS_SYSTEM
                     | PackageParser.PARSE_IS_SYSTEM_DIR
@@ -18397,9 +18410,10 @@
 
     @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out,
-            FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+            FileDescriptor err, String[] args, ShellCallback callback,
+            ResultReceiver resultReceiver) {
         (new PackageManagerShellCommand(this)).exec(
-                this, in, out, err, args, resultReceiver);
+                this, in, out, err, args, callback, resultReceiver);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 13f558e..19bf751 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -67,6 +67,7 @@
 import android.os.ResultReceiver;
 import android.os.SELinux;
 import android.os.ServiceManager;
+import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -3366,13 +3367,14 @@
 
     @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
-            String[] args, ResultReceiver resultReceiver) throws RemoteException {
+            String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
 
         enforceShell();
 
         final long token = injectClearCallingIdentity();
         try {
-            final int status = (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver);
+            final int status = (new MyShellCommand()).exec(this, in, out, err, args, callback,
+                    resultReceiver);
             resultReceiver.send(status, null);
         } finally {
             injectRestoreCallingIdentity(token);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index c1cb032..533d9b5 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -62,6 +62,7 @@
 import android.os.ResultReceiver;
 import android.os.SELinux;
 import android.os.ServiceManager;
+import android.os.ShellCallback;
 import android.os.ShellCommand;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -1705,14 +1706,13 @@
                                         UserRestrictionsUtils
                                                 .readRestrictions(parser, mGuestRestrictions);
                                     }
-                                } else if (parser.getName().equals(TAG_DEVICE_POLICY_RESTRICTIONS)
-                                        ) {
-                                    UserRestrictionsUtils.readRestrictions(parser,
-                                            newDevicePolicyGlobalUserRestrictions);
                                 }
                                 break;
                             }
                         }
+                    } else if (name.equals(TAG_DEVICE_POLICY_RESTRICTIONS)) {
+                        UserRestrictionsUtils.readRestrictions(parser,
+                                newDevicePolicyGlobalUserRestrictions);
                     } else if (name.equals(TAG_GLOBAL_RESTRICTION_OWNER_ID)) {
                         String ownerUserId = parser.getAttributeValue(null, ATTR_ID);
                         if (ownerUserId != null) {
@@ -3209,8 +3209,9 @@
 
     @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out,
-            FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
-        (new Shell()).exec(this, in, out, err, args, resultReceiver);
+            FileDescriptor err, String[] args, ShellCallback callback,
+            ResultReceiver resultReceiver) {
+        (new Shell()).exec(this, in, out, err, args, callback, resultReceiver);
     }
 
     int onShellCommand(Shell shell, String cmd) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b555938..b3430c6 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -182,6 +182,8 @@
     // No longer recommended for desk docks;
     static final boolean ENABLE_DESK_DOCK_HOME_CAPTURE = false;
 
+    static final boolean ALTERNATE_CAR_MODE_NAV_SIZE = false;
+
     static final int SHORT_PRESS_POWER_NOTHING = 0;
     static final int SHORT_PRESS_POWER_GO_TO_SLEEP = 1;
     static final int SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP = 2;
@@ -1373,8 +1375,13 @@
             case LONG_PRESS_BACK_NOTHING:
                 break;
             case LONG_PRESS_BACK_GO_TO_VOICE_ASSIST:
-                Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
-                startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+                final boolean keyguardActive = mKeyguardDelegate == null
+                        ? false
+                        : mKeyguardDelegate.isShowing();
+                if (!keyguardActive) {
+                    Intent intent = new Intent(Intent.ACTION_VOICE_ASSIST);
+                    startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+                }
                 break;
         }
     }
@@ -2451,22 +2458,24 @@
         mNavigationBarWidthForRotationDefault[mSeascapeRotation] =
                 res.getDimensionPixelSize(com.android.internal.R.dimen.navigation_bar_width);
 
-        // Height of the navigation bar when presented horizontally at bottom
-        mNavigationBarHeightForRotationInCarMode[mPortraitRotation] =
-        mNavigationBarHeightForRotationInCarMode[mUpsideDownRotation] =
-                res.getDimensionPixelSize(
-                        com.android.internal.R.dimen.navigation_bar_height_car_mode);
-        mNavigationBarHeightForRotationInCarMode[mLandscapeRotation] =
-        mNavigationBarHeightForRotationInCarMode[mSeascapeRotation] = res.getDimensionPixelSize(
-                com.android.internal.R.dimen.navigation_bar_height_landscape_car_mode);
+        if (ALTERNATE_CAR_MODE_NAV_SIZE) {
+            // Height of the navigation bar when presented horizontally at bottom
+            mNavigationBarHeightForRotationInCarMode[mPortraitRotation] =
+            mNavigationBarHeightForRotationInCarMode[mUpsideDownRotation] =
+                    res.getDimensionPixelSize(
+                            com.android.internal.R.dimen.navigation_bar_height_car_mode);
+            mNavigationBarHeightForRotationInCarMode[mLandscapeRotation] =
+            mNavigationBarHeightForRotationInCarMode[mSeascapeRotation] = res.getDimensionPixelSize(
+                    com.android.internal.R.dimen.navigation_bar_height_landscape_car_mode);
 
-        // Width of the navigation bar when presented vertically along one side
-        mNavigationBarWidthForRotationInCarMode[mPortraitRotation] =
-        mNavigationBarWidthForRotationInCarMode[mUpsideDownRotation] =
-        mNavigationBarWidthForRotationInCarMode[mLandscapeRotation] =
-        mNavigationBarWidthForRotationInCarMode[mSeascapeRotation] =
-                res.getDimensionPixelSize(
-                        com.android.internal.R.dimen.navigation_bar_width_car_mode);
+            // Width of the navigation bar when presented vertically along one side
+            mNavigationBarWidthForRotationInCarMode[mPortraitRotation] =
+            mNavigationBarWidthForRotationInCarMode[mUpsideDownRotation] =
+            mNavigationBarWidthForRotationInCarMode[mLandscapeRotation] =
+            mNavigationBarWidthForRotationInCarMode[mSeascapeRotation] =
+                    res.getDimensionPixelSize(
+                            com.android.internal.R.dimen.navigation_bar_width_car_mode);
+        }
     }
 
     /** {@inheritDoc} */
@@ -2598,7 +2607,7 @@
     }
 
     private int getNavigationBarWidth(int rotation, int uiMode) {
-        if ((uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
+        if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
             return mNavigationBarWidthForRotationInCarMode[rotation];
         } else {
             return mNavigationBarWidthForRotationDefault[rotation];
@@ -2619,7 +2628,7 @@
     }
 
     private int getNavigationBarHeight(int rotation, int uiMode) {
-        if ((uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
+        if (ALTERNATE_CAR_MODE_NAV_SIZE && (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR) {
             return mNavigationBarHeightForRotationInCarMode[rotation];
         } else {
             return mNavigationBarHeightForRotationDefault[rotation];
diff --git a/services/core/java/com/android/server/policy/RecentApplicationsBackground.java b/services/core/java/com/android/server/policy/RecentApplicationsBackground.java
deleted file mode 100644
index 694a110..0000000
--- a/services/core/java/com/android/server/policy/RecentApplicationsBackground.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.policy;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.LinearLayout;
-
-/**
- * A vertical linear layout.  However, instead of drawing the background
- * behnd the items, it draws the background outside the items based on the
- * padding.  If there isn't enough room to draw both, it clips the background
- * instead of the contents.
- */
-public class RecentApplicationsBackground extends LinearLayout {
-    private static final String TAG = "RecentApplicationsBackground";
-
-    private boolean mBackgroundSizeChanged;
-    private Drawable mBackground;
-    private Rect mTmp0 = new Rect();
-    private Rect mTmp1 = new Rect();
-
-    public RecentApplicationsBackground(Context context) {
-        this(context, null);
-        init();
-    }
-
-    public RecentApplicationsBackground(Context context, AttributeSet attrs) {
-        super(context, attrs);
-        init();
-    }
-
-    private void init() {
-        mBackground = getBackground();
-        setBackgroundDrawable(null);
-        setPadding(0, 0, 0, 0);
-        setGravity(Gravity.CENTER);
-    }
-
-    @Override
-    protected boolean setFrame(int left, int top, int right, int bottom) {
-        setWillNotDraw(false);
-        if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
-            mBackgroundSizeChanged = true;
-        }
-        return super.setFrame(left, top, right, bottom);
-    }
-
-    @Override
-    protected boolean verifyDrawable(Drawable who) {
-        return who == mBackground || super.verifyDrawable(who);
-    }
-
-    @Override
-    public void jumpDrawablesToCurrentState() {
-        super.jumpDrawablesToCurrentState();
-        if (mBackground != null) mBackground.jumpToCurrentState();
-    }
-
-    @Override
-    protected void drawableStateChanged() {
-        Drawable d = mBackground;
-        if (d != null && d.isStateful()) {
-            d.setState(getDrawableState());
-        }
-        super.drawableStateChanged();
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        final Drawable background = mBackground;
-        if (background != null) {
-            if (mBackgroundSizeChanged) {
-                mBackgroundSizeChanged = false;
-                Rect chld = mTmp0;
-                Rect bkg = mTmp1;
-                mBackground.getPadding(bkg);
-                getChildBounds(chld);
-                // This doesn't clamp to this view's bounds, which is what we want,
-                // so that the drawing is clipped.
-                final int top = chld.top - bkg.top;
-                final int bottom = chld.bottom + bkg.bottom;
-                // The background here is a gradient that wants to
-                // extend the full width of the screen (whatever that
-                // may be).
-                int left, right;
-                if (false) {
-                    // This limits the width of the drawable.
-                    left = chld.left - bkg.left;
-                    right = chld.right + bkg.right;
-                } else {
-                    // This expands it to full width.
-                    left = 0;
-                    right = getRight();
-                }
-                background.setBounds(left, top, right, bottom);
-            }
-        }
-        mBackground.draw(canvas);
-
-        if (false) {
-            android.graphics.Paint p = new android.graphics.Paint();
-            p.setColor(0x88ffff00);
-            canvas.drawRect(background.getBounds(), p);
-        }
-        canvas.drawARGB((int)(0.75*0xff), 0, 0, 0);
-
-        super.draw(canvas);
-    }
-
-    @Override
-    protected void onAttachedToWindow() {
-        super.onAttachedToWindow();
-        mBackground.setCallback(this);
-        setWillNotDraw(false);
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mBackground.setCallback(null);
-    }
-    
-    private void getChildBounds(Rect r) {
-        r.left = r.top = Integer.MAX_VALUE;
-        r.bottom = r.right = Integer.MIN_VALUE;
-        final int N = getChildCount();
-        for (int i=0; i<N; i++) {
-            View v = getChildAt(i);
-            if (v.getVisibility() == View.VISIBLE) {
-                r.left = Math.min(r.left, v.getLeft());
-                r.top = Math.min(r.top, v.getTop());
-                r.right = Math.max(r.right, v.getRight());
-                r.bottom = Math.max(r.bottom, v.getBottom());
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 552803f..7b7db0e 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -28,23 +28,21 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Slog;
-import android.view.KeyEvent;
 
 import com.android.internal.statusbar.IStatusBar;
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.internal.statusbar.StatusBarIcon;
-import com.android.internal.util.FastPrintWriter;
 import com.android.server.LocalServices;
 import com.android.server.notification.NotificationDelegate;
 import com.android.server.wm.WindowManagerService;
 
 import java.io.FileDescriptor;
-import java.io.FileOutputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
@@ -849,9 +847,9 @@
 
     @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
-            String[] args, ResultReceiver resultReceiver) throws RemoteException {
+            String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
         (new StatusBarShellCommand(this)).exec(
-                this, in, out, err, args, resultReceiver);
+                this, in, out, err, args, callback, resultReceiver);
     }
 
     // ================================================================================
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index d219aed..353d663 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -872,6 +872,11 @@
                         } catch (RemoteException e) {
                         }
                     }
+                    final Intent lockIntent = new Intent(Intent.ACTION_DEVICE_LOCKED_CHANGED);
+                    lockIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    lockIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+                    mContext.sendBroadcastAsUser(lockIntent, UserHandle.SYSTEM,
+                            Manifest.permission.TRUST_LISTENER, /* options */ null);
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateService.java b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
index 846169c..43cdf59 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateService.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateService.java
@@ -25,10 +25,10 @@
 import android.os.PatternMatcher;
 import android.os.Process;
 import android.os.ResultReceiver;
+import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.util.Slog;
 import android.webkit.IWebViewUpdateService;
-import android.webkit.WebViewFactory;
 import android.webkit.WebViewProviderInfo;
 import android.webkit.WebViewProviderResponse;
 
@@ -140,9 +140,10 @@
 
         @Override
         public void onShellCommand(FileDescriptor in, FileDescriptor out,
-                FileDescriptor err, String[] args, ResultReceiver resultReceiver) {
+                FileDescriptor err, String[] args, ShellCallback callback,
+                ResultReceiver resultReceiver) {
             (new WebViewUpdateServiceShellCommand(this)).exec(
-                    this, in, out, err, args, resultReceiver);
+                    this, in, out, err, args, callback, resultReceiver);
         }
 
 
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index e4ec295..cd46165 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -374,6 +374,7 @@
 
     void goodToGo(AppWindowAnimator topOpeningAppAnimator, AppWindowAnimator topClosingAppAnimator,
             ArraySet<AppWindowToken> openingApps, ArraySet<AppWindowToken> closingApps) {
+        int appTransition = mNextAppTransition;
         mNextAppTransition = TRANSIT_UNSET;
         mAppTransitionState = APP_STATE_RUNNING;
         notifyAppTransitionStartingLocked(
@@ -382,7 +383,7 @@
                 topOpeningAppAnimator != null ? topOpeningAppAnimator.animation : null,
                 topClosingAppAnimator != null ? topClosingAppAnimator.animation : null);
         mService.getDefaultDisplayContentLocked().getDockedDividerController()
-                .notifyAppTransitionStarting(openingApps);
+                .notifyAppTransitionStarting(openingApps, appTransition);
 
         // Prolong the start for the transition when docking a task from recents, unless recents
         // ended it already then we don't need to wait.
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index ba26e13..508cf24 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -35,6 +35,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_ACTIVITY_DRAWN;
+import static com.android.server.wm.WindowManagerService.H.NOTIFY_STARTING_WINDOW_DRAWN;
 import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_NONE;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.logWithStack;
@@ -91,19 +92,22 @@
     // an activity have been drawn, so they can be made visible together
     // at the same time.
     // initialize so that it doesn't match mTransactionSequence which is an int.
-    long lastTransactionSequence = Long.MIN_VALUE;
-    int numInterestingWindows;
-    int numDrawnWindows;
+    private long mLastTransactionSequence = Long.MIN_VALUE;
+    private int mNumInterestingWindows;
+    private int mNumDrawnWindows;
     boolean inPendingTransaction;
     boolean allDrawn;
     // Set to true when this app creates a surface while in the middle of an animation. In that
     // case do not clear allDrawn until the animation completes.
     boolean deferClearAllDrawn;
 
-    // These are to track the app's real drawing status if there were no saved surfaces.
+    /**
+     * These are to track the app's real drawing status if there were no saved surfaces.
+     * @see #updateDrawnWindowStates
+     */
     boolean allDrawnExcludingSaved;
-    int numInterestingWindowsExcludingSaved;
-    int numDrawnWindowsExcludingSaved;
+    private int mNumInterestingWindowsExcludingSaved;
+    private int mNumDrawnWindowsExcludingSaved;
 
     // Is this window's surface needed?  This is almost like hidden, except
     // it will sometimes be true a little earlier: when the token has
@@ -118,7 +122,7 @@
     boolean reportedVisible;
 
     // Last drawn state we reported to the app token.
-    boolean reportedDrawn;
+    private boolean reportedDrawn;
 
     // Set to true when the token has been removed from the window mgr.
     boolean removed;
@@ -146,7 +150,7 @@
 
     boolean mAppStopped;
     int mRotationAnimationHint;
-    int mPendingRelaunchCount;
+    private int mPendingRelaunchCount;
 
     private ArrayList<WindowSurfaceController.SurfaceControlWithBackground> mSurfaceViewBackgrounds =
         new ArrayList<>();
@@ -364,11 +368,9 @@
 
     @Override
     boolean isVisible() {
-        if (hidden) {
-            // TODO: Should this be checking hiddenRequested instead of hidden?
-            return false;
-        }
-        return super.isVisible();
+        // If the app token isn't hidden then it is considered visible and there is no need to check
+        // its children windows to see if they are visible.
+        return !hidden;
     }
 
     @Override
@@ -429,7 +431,7 @@
         for (int i = 0; i < displayList.size(); i++) {
             final DisplayContent displayContent = displayList.get(i);
             mService.mLayersController.assignLayersLocked(displayContent.getWindowList());
-            displayContent.layoutNeeded = true;
+            displayContent.setLayoutNeeded();
         }
     }
 
@@ -729,9 +731,7 @@
 
         if (mTask.mPreparedFrozenMergedConfig.equals(Configuration.EMPTY)) {
             // We didn't call prepareFreezingBounds on the task, so use the current value.
-            final Configuration config = new Configuration(mService.mGlobalConfiguration);
-            config.updateFrom(mTask.mOverrideConfig);
-            mFrozenMergedConfig.offer(config);
+            mFrozenMergedConfig.offer(new Configuration(mTask.getConfiguration()));
         } else {
             mFrozenMergedConfig.offer(new Configuration(mTask.mPreparedFrozenMergedConfig));
         }
@@ -792,6 +792,9 @@
     void overridePlayingAppAnimations(Animation a) {
         if (mAppAnimator.isAnimating()) {
             final WindowState win = findMainWindow();
+            if (win == null) {
+                return;
+            }
             final int width = win.mContainingFrame.width();
             final int height = win.mContainingFrame.height();
             mAppAnimator.setAnimation(a, width, height, false, STACK_CLIP_NONE);
@@ -957,7 +960,7 @@
 
             mService.updateFocusedWindowLocked(
                     UPDATE_FOCUS_WILL_PLACE_SURFACES, true /*updateInputWindows*/);
-            mService.getDefaultDisplayContentLocked().layoutNeeded = true;
+            mService.getDefaultDisplayContentLocked().setLayoutNeeded();
             mService.mWindowPlacerLocked.performSurfacePlacement();
             Binder.restoreCallingIdentity(origId);
             return true;
@@ -1039,7 +1042,7 @@
             stopFreezingScreen(false, true);
             if (DEBUG_ORIENTATION) Slog.i(TAG,
                     "Setting mOrientationChangeComplete=true because wtoken " + this
-                    + " numInteresting=" + numInterestingWindows + " numDrawn=" + numDrawnWindows);
+                    + " numInteresting=" + mNumInterestingWindows + " numDrawn=" + mNumDrawnWindows);
             // This will set mOrientationChangeComplete and cause a pass through layout.
             setAppLayoutChanges(FINISH_LAYOUT_REDO_WALLPAPER,
                     "checkAppWindowsReadyToShow: freezingScreen", displayId);
@@ -1053,30 +1056,28 @@
         }
     }
 
-    @Override
-    void updateAllDrawn(int displayId) {
-        final DisplayContent displayContent = mService.mRoot.getDisplayContentOrCreate(displayId);
-
+    void updateAllDrawn(DisplayContent dc) {
         if (!allDrawn) {
-            final int numInteresting = numInterestingWindows;
-            if (numInteresting > 0 && numDrawnWindows >= numInteresting) {
+            final int numInteresting = mNumInterestingWindows;
+            if (numInteresting > 0 && mNumDrawnWindows >= numInteresting) {
                 if (DEBUG_VISIBILITY) Slog.v(TAG, "allDrawn: " + this
-                        + " interesting=" + numInteresting + " drawn=" + numDrawnWindows);
+                        + " interesting=" + numInteresting + " drawn=" + mNumDrawnWindows);
                 allDrawn = true;
                 // Force an additional layout pass where
                 // WindowStateAnimator#commitFinishDrawingLocked() will call performShowLocked().
-                displayContent.layoutNeeded = true;
+                dc.setLayoutNeeded();
                 mService.mH.obtainMessage(NOTIFY_ACTIVITY_DRAWN, token).sendToTarget();
             }
         }
+
         if (!allDrawnExcludingSaved) {
-            int numInteresting = numInterestingWindowsExcludingSaved;
-            if (numInteresting > 0 && numDrawnWindowsExcludingSaved >= numInteresting) {
+            int numInteresting = mNumInterestingWindowsExcludingSaved;
+            if (numInteresting > 0 && mNumDrawnWindowsExcludingSaved >= numInteresting) {
                 if (DEBUG_VISIBILITY) Slog.v(TAG, "allDrawnExcludingSaved: " + this
                         + " interesting=" + numInteresting
-                        + " drawn=" + numDrawnWindowsExcludingSaved);
+                        + " drawn=" + mNumDrawnWindowsExcludingSaved);
                 allDrawnExcludingSaved = true;
-                displayContent.layoutNeeded = true;
+                dc.setLayoutNeeded();
                 if (isAnimatingInvisibleWithSavedSurface()
                         && !mService.mFinishedEarlyAnim.contains(this)) {
                     mService.mFinishedEarlyAnim.add(this);
@@ -1085,6 +1086,87 @@
         }
     }
 
+    /**
+     * Updated this app token tracking states for interesting and drawn windows based on the window.
+     *
+     * @return Returns true if the input window is considered interesting and drawn while all the
+     *         windows in this app token where not considered drawn as of the last pass.
+     */
+    boolean updateDrawnWindowStates(WindowState w) {
+        if (DEBUG_STARTING_WINDOW && w == startingWindow) {
+            Slog.d(TAG, "updateWindows: starting " + w + " isOnScreen=" + w.isOnScreen()
+                    + " allDrawn=" + allDrawn + " freezingScreen=" + mAppAnimator.freezingScreen);
+        }
+
+        if (allDrawn && allDrawnExcludingSaved && !mAppAnimator.freezingScreen) {
+            return false;
+        }
+
+        if (mLastTransactionSequence != mService.mTransactionSequence) {
+            mLastTransactionSequence = mService.mTransactionSequence;
+            mNumInterestingWindows = mNumDrawnWindows = 0;
+            mNumInterestingWindowsExcludingSaved = 0;
+            mNumDrawnWindowsExcludingSaved = 0;
+            startingDisplayed = false;
+        }
+
+        final WindowStateAnimator winAnimator = w.mWinAnimator;
+
+        boolean isInterestingAndDrawn = false;
+
+        if (!allDrawn && w.mightAffectAllDrawn(false /* visibleOnly */)) {
+            if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) {
+                Slog.v(TAG, "Eval win " + w + ": isDrawn=" + w.isDrawnLw()
+                        + ", isAnimationSet=" + winAnimator.isAnimationSet());
+                if (!w.isDrawnLw()) {
+                    Slog.v(TAG, "Not displayed: s=" + winAnimator.mSurfaceController
+                            + " pv=" + w.mPolicyVisibility
+                            + " mDrawState=" + winAnimator.drawStateToString()
+                            + " ph=" + w.isParentWindowHidden() + " th=" + hiddenRequested
+                            + " a=" + winAnimator.mAnimating);
+                }
+            }
+
+            if (w != startingWindow) {
+                if (w.isInteresting()) {
+                    mNumInterestingWindows++;
+                    if (w.isDrawnLw()) {
+                        mNumDrawnWindows++;
+
+                        if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) Slog.v(TAG, "tokenMayBeDrawn: "
+                                + this + " w=" + w + " numInteresting=" + mNumInterestingWindows
+                                + " freezingScreen=" + mAppAnimator.freezingScreen
+                                + " mAppFreezing=" + w.mAppFreezing);
+
+                        isInterestingAndDrawn = true;
+                    }
+                }
+            } else if (w.isDrawnLw()) {
+                mService.mH.sendEmptyMessage(NOTIFY_STARTING_WINDOW_DRAWN);
+                startingDisplayed = true;
+            }
+        }
+
+        if (!allDrawnExcludingSaved && w.mightAffectAllDrawn(true /* visibleOnly */)) {
+            if (w != startingWindow && w.isInteresting()) {
+                mNumInterestingWindowsExcludingSaved++;
+                if (w.isDrawnLw() && !w.isAnimatingWithSavedSurface()) {
+                    mNumDrawnWindowsExcludingSaved++;
+
+                    if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) Slog.v(TAG,
+                            "tokenMayBeDrawnExcludingSaved: " + this + " w=" + w
+                            + " numInteresting=" + mNumInterestingWindowsExcludingSaved
+                            + " freezingScreen=" + mAppAnimator.freezingScreen
+                            + " mAppFreezing=" + w.mAppFreezing);
+
+                    isInterestingAndDrawn = true;
+                }
+            }
+        }
+
+        return isInterestingAndDrawn;
+    }
+
     @Override
     void stepAppWindowsAnimation(long currentTime, int displayId) {
         mAppAnimator.wasAnimating = mAppAnimator.animating;
@@ -1146,11 +1228,11 @@
         if (mAppStopped) {
             pw.print(prefix); pw.print("mAppStopped="); pw.println(mAppStopped);
         }
-        if (numInterestingWindows != 0 || numDrawnWindows != 0
+        if (mNumInterestingWindows != 0 || mNumDrawnWindows != 0
                 || allDrawn || mAppAnimator.allDrawn) {
-            pw.print(prefix); pw.print("numInterestingWindows=");
-                    pw.print(numInterestingWindows);
-                    pw.print(" numDrawnWindows="); pw.print(numDrawnWindows);
+            pw.print(prefix); pw.print("mNumInterestingWindows=");
+                    pw.print(mNumInterestingWindows);
+                    pw.print(" mNumDrawnWindows="); pw.print(mNumDrawnWindows);
                     pw.print(" inPendingTransaction="); pw.print(inPendingTransaction);
                     pw.print(" allDrawn="); pw.print(allDrawn);
                     pw.print(" (animator="); pw.print(mAppAnimator.allDrawn);
diff --git a/services/core/java/com/android/server/wm/DimLayerController.java b/services/core/java/com/android/server/wm/DimLayerController.java
index 7f97c46..da2c6a7 100644
--- a/services/core/java/com/android/server/wm/DimLayerController.java
+++ b/services/core/java/com/android/server/wm/DimLayerController.java
@@ -256,7 +256,7 @@
                 // on whether a dim layer is showing or not.
                 if (targetAlpha == 0) {
                     mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
-                    mDisplayContent.layoutNeeded = true;
+                    mDisplayContent.setLayoutNeeded();
                 }
             }
         } else if (state.dimLayer.getLayer() != dimLayer) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 66682d8..42e9e23 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -38,6 +38,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
@@ -46,6 +47,7 @@
 
 import android.annotation.NonNull;
 import android.app.ActivityManager.StackId;
+import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.Region.Op;
@@ -100,7 +102,7 @@
     private Rect mContentRect = new Rect();
 
     // Accessed directly by all users.
-    boolean layoutNeeded;
+    private boolean mLayoutNeeded;
     int pendingLayoutChanges;
     final boolean isDefaultDisplay;
 
@@ -209,18 +211,26 @@
         return null;
     }
 
-    /** Callback used to notify about configuration changes. */
-    void onConfigurationChanged(@NonNull List<Integer> changedStackList) {
+    @Override
+    void onConfigurationChanged(Configuration newParentConfig) {
+        super.onConfigurationChanged(newParentConfig);
+
         // The display size information is heavily dependent on the resources in the current
         // configuration, so we need to reconfigure it every time the configuration changes.
         // See {@link PhoneWindowManager#setInitialDisplaySize}...sigh...
         mService.reconfigureDisplayLocked(this);
 
         getDockedDividerController().onConfigurationChanged();
+    }
 
-        for (int i = 0; i < mChildren.size(); i++) {
+    /**
+     * Callback used to trigger bounds update after configuration change and get ids of stacks whose
+     * bounds were updated.
+     */
+    void updateStackBoundsAfterConfigChange(@NonNull List<Integer> changedStackList) {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
             final TaskStack stack = mChildren.get(i);
-            if (stack.onConfigurationChanged()) {
+            if (stack.updateBoundsAfterConfigChange()) {
                 changedStackList.add(stack.mStackId);
             }
         }
@@ -230,10 +240,6 @@
         super.checkAppWindowsReadyToShow(mDisplayId);
     }
 
-    void updateAllDrawn() {
-        super.updateAllDrawn(mDisplayId);
-    }
-
     void stepAppWindowsAnimation(long currentTime) {
         super.stepAppWindowsAnimation(currentTime, mDisplayId);
     }
@@ -367,7 +373,7 @@
             }
         }
         addChild(stack, addIndex);
-        layoutNeeded = true;
+        setLayoutNeeded();
     }
 
     /**
@@ -684,8 +690,8 @@
             pw.print("x"); pw.print(mDisplayInfo.smallestNominalAppHeight);
             pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth);
             pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight);
-            pw.print(subPrefix); pw.print("deferred="); pw.print(mDeferredRemoval);
-                pw.print(" layoutNeeded="); pw.println(layoutNeeded);
+            pw.println(subPrefix + "deferred=" + mDeferredRemoval
+                    + " mLayoutNeeded=" + mLayoutNeeded);
 
         pw.println();
         pw.println("  Application tokens in top down Z order:");
@@ -778,7 +784,8 @@
         for (int i = 0; i < windowCount; i++) {
             WindowState window = windows.get(i);
             if (window.mAttrs.type == TYPE_TOAST && window.mOwnerUid == uid
-                    && !window.mPermanentlyHidden && !window.mAnimatingExit) {
+                    && !window.mPermanentlyHidden && !window.mAnimatingExit
+                    && !window.mRemoveOnExit) {
                 return false;
             }
         }
@@ -1090,7 +1097,7 @@
 
         i -= lastBelow;
         if (i != numRemoved) {
-            layoutNeeded = true;
+            setLayoutNeeded();
             Slog.w(TAG_WM, "On display=" + mDisplayId + " Rebuild removed " + numRemoved
                     + " windows but added " + i + " rebuildAppWindowListLocked() "
                     + " callers=" + Debug.getCallers(10));
@@ -1127,6 +1134,20 @@
         return windowList;
     }
 
+    void setLayoutNeeded() {
+        if (DEBUG_LAYOUT) Slog.w(TAG_WM, "setLayoutNeeded: callers=" + Debug.getCallers(3));
+        mLayoutNeeded = true;
+    }
+
+    void clearLayoutNeeded() {
+        if (DEBUG_LAYOUT) Slog.w(TAG_WM, "clearLayoutNeeded: callers=" + Debug.getCallers(3));
+        mLayoutNeeded = false;
+    }
+
+    boolean isLayoutNeeded() {
+        return mLayoutNeeded;
+    }
+
     private int addAppWindowExisting(WindowState win, WindowList tokenWindowList) {
 
         int tokenWindowsPos;
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index ef8f492..5854197 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -29,6 +29,7 @@
 import static android.view.WindowManager.DOCKED_TOP;
 import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
 import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
+import static com.android.server.wm.AppTransition.TRANSIT_NONE;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED;
@@ -153,7 +154,7 @@
         // If the bounds are fullscreen, return the value of the fullscreen configuration
         if (bounds == null || (bounds.left == 0 && bounds.top == 0
                 && bounds.right == di.logicalWidth && bounds.bottom == di.logicalHeight)) {
-            return mService.mGlobalConfiguration.smallestScreenWidthDp;
+            return mDisplayContent.getConfiguration().smallestScreenWidthDp;
         }
         final int baseDisplayWidth = mDisplayContent.mBaseDisplayWidth;
         final int baseDisplayHeight = mDisplayContent.mBaseDisplayHeight;
@@ -190,7 +191,7 @@
     }
 
     private void initSnapAlgorithmForRotations() {
-        final Configuration baseConfig = mService.mGlobalConfiguration;
+        final Configuration baseConfig = mDisplayContent.getConfiguration();
 
         // Initialize the snap algorithms for all 4 screen orientations.
         final Configuration config = new Configuration();
@@ -493,7 +494,7 @@
         checkMinimizeChanged(false /* animate */);
     }
 
-    void notifyAppTransitionStarting(ArraySet<AppWindowToken> openingApps) {
+    void notifyAppTransitionStarting(ArraySet<AppWindowToken> openingApps, int appTransition) {
         final boolean wasMinimized = mMinimizedDock;
         checkMinimizeChanged(true /* animate */);
 
@@ -502,7 +503,8 @@
         // any case that was missed in ActivityStarter.postStartActivityUncheckedProcessing because
         // we couldn't retrace the launch of the app in the docked stack to the launch from
         // homescreen.
-        if (wasMinimized && mMinimizedDock && containsAppInDockedStack(openingApps)) {
+        if (wasMinimized && mMinimizedDock && containsAppInDockedStack(openingApps)
+                && appTransition != TRANSIT_NONE) {
             mService.showRecentApps(true /* fromHome */);
         }
     }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 8f533fb1..1700bca 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -29,7 +29,6 @@
 import android.provider.Settings;
 import android.util.EventLog;
 import android.util.Slog;
-import android.util.SparseArray;
 import android.util.SparseIntArray;
 import android.view.Display;
 import android.view.DisplayInfo;
@@ -42,12 +41,12 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.LinkedList;
 
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
 import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
 import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
@@ -66,7 +65,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TOKEN_MOVEMENT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
@@ -77,7 +75,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.NOTIFY_STARTING_WINDOW_DRAWN;
 import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
 import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
 import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
@@ -137,6 +134,8 @@
 
     private final ArrayList<Integer> mChangedStackList = new ArrayList();
 
+    private final LinkedList<AppWindowToken> mTmpUpdateAllDrawn = new LinkedList();
+
     // State for the RemoteSurfaceTrace system used in testing. If this is enabled SurfaceControl
     // instances will be replaced with an instance that writes a binary representation of all
     // commands to mSurfaceTraceFd.
@@ -265,11 +264,11 @@
         return bounds;
     }
 
-    boolean layoutNeeded() {
+    boolean isLayoutNeeded() {
         final int numDisplays = mChildren.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
             final DisplayContent displayContent = mChildren.get(displayNdx);
-            if (displayContent.layoutNeeded) {
+            if (displayContent.isLayoutNeeded()) {
                 return true;
             }
         }
@@ -347,18 +346,35 @@
         }
     }
 
-    int[] onConfigurationChanged(Configuration config) {
+    /** Set new config and return array of ids of stacks that were changed during update. */
+    int[] setGlobalConfigurationIfNeeded(Configuration newConfiguration) {
+        final boolean configChanged = getConfiguration().diff(newConfiguration) != 0;
+        if (!configChanged) {
+            return null;
+        }
+        onConfigurationChanged(newConfiguration);
+        return updateStackBoundsAfterConfigChange();
+    }
+
+    @Override
+    void onConfigurationChanged(Configuration newParentConfig) {
         prepareFreezingTaskBounds();
-        mService.mGlobalConfiguration = new Configuration(config);
+        super.onConfigurationChanged(newParentConfig);
 
         mService.mPolicy.onConfigurationChanged();
+    }
 
+    /**
+     * Callback used to trigger bounds update after configuration change and get ids of stacks whose
+     * bounds were updated.
+     */
+    int[] updateStackBoundsAfterConfigChange() {
         mChangedStackList.clear();
 
         final int numDisplays = mChildren.size();
         for (int i = 0; i < numDisplays; ++i) {
             final DisplayContent dc = mChildren.get(i);
-            dc.onConfigurationChanged(mChangedStackList);
+            dc.updateStackBoundsAfterConfigChange(mChangedStackList);
         }
 
         return mChangedStackList.isEmpty() ? null : ArrayUtils.convertToIntArray(mChangedStackList);
@@ -691,7 +707,7 @@
                 ">>> OPEN TRANSACTION performLayoutAndPlaceSurfaces");
         mService.openSurfaceTransaction();
         try {
-            mService.mRoot.applySurfaceChangesTransaction(recoveringMemory, defaultDw, defaultDh);
+            applySurfaceChangesTransaction(recoveringMemory, defaultDw, defaultDh);
         } catch (RuntimeException e) {
             Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
         } finally {
@@ -753,7 +769,7 @@
             }
         }
 
-        if (layoutNeeded()) {
+        if (isLayoutNeeded()) {
             defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_LAYOUT;
             if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats("mLayoutNeeded",
                     defaultDisplay.pendingLayoutChanges);
@@ -840,13 +856,13 @@
 
         if (wallpaperDestroyed) {
             defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
-            defaultDisplay.layoutNeeded = true;
+            defaultDisplay.setLayoutNeeded();
         }
 
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
             final DisplayContent displayContent = mChildren.get(displayNdx);
             if (displayContent.pendingLayoutChanges != 0) {
-                displayContent.layoutNeeded = true;
+                displayContent.setLayoutNeeded();
             }
         }
 
@@ -901,8 +917,8 @@
         }
 
         if (mService.mWaitingForDrawnCallback != null ||
-                (mOrientationChangeComplete && !defaultDisplay.layoutNeeded &&
-                        !mUpdateRotation)) {
+                (mOrientationChangeComplete && !defaultDisplay.isLayoutNeeded()
+                        && !mUpdateRotation)) {
             mService.checkDrawnWindowsLocked();
         }
 
@@ -925,7 +941,7 @@
 
             for (DisplayContent displayContent : displayList) {
                 mService.mLayersController.assignLayersLocked(displayContent.getWindowList());
-                displayContent.layoutNeeded = true;
+                displayContent.setLayoutNeeded();
             }
         }
 
@@ -951,7 +967,7 @@
     }
 
     // TODO: Super crazy long method that should be broken down...
-    void applySurfaceChangesTransaction(boolean recoveringMemory, int defaultDw, int defaultDh) {
+    private void applySurfaceChangesTransaction(boolean recoveringMemory, int defaultDw, int defaultDh) {
         mHoldScreenWindow = null;
         mObsuringWindow = null;
 
@@ -974,7 +990,6 @@
         final int count = mChildren.size();
         for (int j = 0; j < count; ++j) {
             final DisplayContent dc = mChildren.get(j);
-            boolean updateAllDrawn = false;
             WindowList windows = dc.getWindowList();
             DisplayInfo displayInfo = dc.getDisplayInfo();
             final int displayId = dc.getDisplayId();
@@ -987,13 +1002,14 @@
             mDisplayHasContent = false;
             mPreferredRefreshRate = 0;
             mPreferredModeId = 0;
+            mTmpUpdateAllDrawn.clear();
 
             int repeats = 0;
             do {
                 repeats++;
                 if (repeats > 6) {
                     Slog.w(TAG, "Animation repeat aborted after too many iterations");
-                    dc.layoutNeeded = false;
+                    dc.clearLayoutNeeded();
                     break;
                 }
 
@@ -1003,20 +1019,20 @@
                 if ((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0
                         && mService.mWallpaperControllerLocked.adjustWallpaperWindows()) {
                     mService.mLayersController.assignLayersLocked(windows);
-                    dc.layoutNeeded = true;
+                    dc.setLayoutNeeded();
                 }
 
                 if (isDefaultDisplay
                         && (dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_CONFIG) != 0) {
                     if (DEBUG_LAYOUT) Slog.v(TAG, "Computing new config from layout");
                     if (mService.updateOrientationFromAppTokensLocked(true)) {
-                        dc.layoutNeeded = true;
+                        dc.setLayoutNeeded();
                         mService.mH.sendEmptyMessage(SEND_NEW_CONFIGURATION);
                     }
                 }
 
                 if ((dc.pendingLayoutChanges & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
-                    dc.layoutNeeded = true;
+                    dc.setLayoutNeeded();
                 }
 
                 // FIRST LOOP: Perform a layout, if needed.
@@ -1070,45 +1086,14 @@
                 if (isDefaultDisplay && obscuredChanged && w.isVisibleLw()
                         && mService.mWallpaperControllerLocked.isWallpaperTarget(w)) {
                     // This is the wallpaper target and its obscured state changed... make sure the
-                    // current wallaper's visibility has been updated accordingly.
+                    // current wallpaper's visibility has been updated accordingly.
                     mService.mWallpaperControllerLocked.updateWallpaperVisibility();
                 }
 
+                w.handleWindowMovedIfNeeded();
+
                 final WindowStateAnimator winAnimator = w.mWinAnimator;
 
-                // If the window has moved due to its containing content frame changing, then
-                // notify the listeners and optionally animate it. Simply checking a change of
-                // position is not enough, because being move due to dock divider is not a trigger
-                // for animation.
-                if (w.hasMoved()) {
-                    // Frame has moved, containing content frame has also moved, and we're not
-                    // currently animating... let's do something.
-                    final int left = w.mFrame.left;
-                    final int top = w.mFrame.top;
-                    final boolean adjustedForMinimizedDockOrIme = task != null
-                            && (task.mStack.isAdjustedForMinimizedDockedStack()
-                            || task.mStack.isAdjustedForIme());
-                    if (mService.okToDisplay()
-                            && (w.mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
-                            && !w.isDragResizing() && !adjustedForMinimizedDockOrIme
-                            && (task == null || w.getTask().mStack.hasMovementAnimations())
-                            && !w.mWinAnimator.mLastHidden) {
-                        winAnimator.setMoveAnimation(left, top);
-                    }
-
-                    //TODO (multidisplay): Accessibility supported only for the default display.
-                    if (mService.mAccessibilityController != null
-                            && displayId == Display.DEFAULT_DISPLAY) {
-                        mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
-                    }
-
-                    try {
-                        w.mClient.moved(left, top);
-                    } catch (RemoteException e) {
-                    }
-                    w.mMovedByResize = false;
-                }
-
                 //Slog.i(TAG, "Window " + this + " clearing mContentChanged - done placing");
                 w.mContentChanged = false;
 
@@ -1155,71 +1140,10 @@
                 }
 
                 final AppWindowToken atoken = w.mAppToken;
-                if (DEBUG_STARTING_WINDOW && atoken != null && w == atoken.startingWindow) {
-                    Slog.d(TAG, "updateWindows: starting " + w
-                            + " isOnScreen=" + w.isOnScreen() + " allDrawn=" + atoken.allDrawn
-                            + " freezingScreen=" + atoken.mAppAnimator.freezingScreen);
-                }
-                if (atoken != null && (!atoken.allDrawn || !atoken.allDrawnExcludingSaved
-                        || atoken.mAppAnimator.freezingScreen)) {
-                    if (atoken.lastTransactionSequence != mService.mTransactionSequence) {
-                        atoken.lastTransactionSequence = mService.mTransactionSequence;
-                        atoken.numInterestingWindows = atoken.numDrawnWindows = 0;
-                        atoken.numInterestingWindowsExcludingSaved = 0;
-                        atoken.numDrawnWindowsExcludingSaved = 0;
-                        atoken.startingDisplayed = false;
-                    }
-                    if (!atoken.allDrawn && w.mightAffectAllDrawn(false /* visibleOnly */)) {
-                        if (DEBUG_VISIBILITY || DEBUG_ORIENTATION) {
-                            Slog.v(TAG, "Eval win " + w + ": isDrawn="
-                                    + w.isDrawnLw()
-                                    + ", isAnimationSet=" + winAnimator.isAnimationSet());
-                            if (!w.isDrawnLw()) {
-                                Slog.v(TAG, "Not displayed: s="
-                                        + winAnimator.mSurfaceController
-                                        + " pv=" + w.mPolicyVisibility
-                                        + " mDrawState=" + winAnimator.drawStateToString()
-                                        + " ph=" + w.isParentWindowHidden()
-                                        + " th=" + atoken.hiddenRequested
-                                        + " a=" + winAnimator.mAnimating);
-                            }
-                        }
-                        if (w != atoken.startingWindow) {
-                            if (w.isInteresting()) {
-                                atoken.numInterestingWindows++;
-                                if (w.isDrawnLw()) {
-                                    atoken.numDrawnWindows++;
-                                    if (DEBUG_VISIBILITY || DEBUG_ORIENTATION)
-                                        Slog.v(TAG, "tokenMayBeDrawn: " + atoken
-                                                + " w=" + w + " numInteresting="
-                                                + atoken.numInterestingWindows
-                                                + " freezingScreen="
-                                                + atoken.mAppAnimator.freezingScreen
-                                                + " mAppFreezing=" + w.mAppFreezing);
-                                    updateAllDrawn = true;
-                                }
-                            }
-                        } else if (w.isDrawnLw()) {
-                            mService.mH.sendEmptyMessage(NOTIFY_STARTING_WINDOW_DRAWN);
-                            atoken.startingDisplayed = true;
-                        }
-                    }
-                    if (!atoken.allDrawnExcludingSaved
-                            && w.mightAffectAllDrawn(true /* visibleOnly */)) {
-                        if (w != atoken.startingWindow && w.isInteresting()) {
-                            atoken.numInterestingWindowsExcludingSaved++;
-                            if (w.isDrawnLw() && !w.isAnimatingWithSavedSurface()) {
-                                atoken.numDrawnWindowsExcludingSaved++;
-                                if (DEBUG_VISIBILITY || DEBUG_ORIENTATION)
-                                    Slog.v(TAG, "tokenMayBeDrawnExcludingSaved: " + atoken
-                                            + " w=" + w + " numInteresting="
-                                            + atoken.numInterestingWindowsExcludingSaved
-                                            + " freezingScreen="
-                                            + atoken.mAppAnimator.freezingScreen
-                                            + " mAppFreezing=" + w.mAppFreezing);
-                                updateAllDrawn = true;
-                            }
-                        }
+                if (atoken != null) {
+                    final boolean updateAllDrawn = atoken.updateDrawnWindowStates(w);
+                    if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(atoken)) {
+                        mTmpUpdateAllDrawn.add(atoken);
                     }
                 }
 
@@ -1228,7 +1152,7 @@
                     focusDisplayed = true;
                 }
 
-                mService.updateResizingWindows(w);
+                w.updateResizingWindowIfNeeded();
             }
 
             mService.mDisplayManagerInternal.setDisplayProperties(displayId,
@@ -1239,10 +1163,11 @@
 
             dc.stopDimmingIfNeeded();
 
-            if (updateAllDrawn) {
+            while (!mTmpUpdateAllDrawn.isEmpty()) {
+                final AppWindowToken atoken = mTmpUpdateAllDrawn.removeLast();
                 // See if any windows have been drawn, so they (and others associated with them)
                 // can now be shown.
-                dc.updateAllDrawn();
+                atoken.updateAllDrawn(dc);
             }
         }
 
@@ -1407,14 +1332,14 @@
     }
 
     void dumpLayoutNeededDisplayIds(PrintWriter pw) {
-        if (!layoutNeeded()) {
+        if (!isLayoutNeeded()) {
             return;
         }
-        pw.print("  layoutNeeded on displays=");
+        pw.print("  mLayoutNeeded on displays=");
         final int count = mChildren.size();
         for (int displayNdx = 0; displayNdx < count; ++displayNdx) {
             final DisplayContent displayContent = mChildren.get(displayNdx);
-            if (displayContent.layoutNeeded) {
+            if (displayContent.isLayoutNeeded()) {
                 pw.print(displayContent.getDisplayId());
             }
         }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 9e8c609..f6598c1 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -70,12 +70,6 @@
     // Whether mBounds is fullscreen
     private boolean mFillsParent = true;
 
-    /**
-     * Contains configurations settings that are different from the parent configuration due to
-     * stack specific operations. E.g. {@link #setBounds}.
-     */
-    Configuration mOverrideConfig = Configuration.EMPTY;
-
     // For comparison with DisplayContent bounds.
     private Rect mTmpRect = new Rect();
     // For handling display rotations.
@@ -120,8 +114,9 @@
             }
         }
 
-        if (wtoken.mParent != null) {
-            wtoken.mParent.removeChild(wtoken);
+        final WindowContainer parent = wtoken.getParent();
+        if (parent != null) {
+            parent.removeChild(wtoken);
         }
         addChild(wtoken, addPos);
         wtoken.mTask = this;
@@ -153,7 +148,7 @@
         if (content != null) {
             content.mDimLayerController.removeDimLayerUser(this);
         }
-        mParent.removeChild(this);
+        getParent().removeChild(this);
         mService.mTaskIdToTask.delete(mTaskId);
     }
 
@@ -165,7 +160,7 @@
         if (DEBUG_STACK) Slog.i(TAG, "moveTaskToStack: removing taskId=" + mTaskId
                 + " from stack=" + mStack);
         EventLog.writeEvent(EventLogTags.WM_TASK_REMOVED, mTaskId, "moveTask");
-        mParent.removeChild(this);
+        getParent().removeChild(this);
         stack.addTask(this, toTop);
     }
 
@@ -254,7 +249,7 @@
         if (displayContent != null) {
             displayContent.mDimLayerController.updateDimLayer(this);
         }
-        mOverrideConfig = mFillsParent ? Configuration.EMPTY : overrideConfig;
+        onOverrideConfigurationChanged(mFillsParent ? Configuration.EMPTY : overrideConfig);
         return boundsChange;
     }
 
@@ -321,8 +316,7 @@
      */
     void prepareFreezingBounds() {
         mPreparedFrozenBounds.set(mBounds);
-        mPreparedFrozenMergedConfig.setTo(mService.mGlobalConfiguration);
-        mPreparedFrozenMergedConfig.updateFrom(mOverrideConfig);
+        mPreparedFrozenMergedConfig.setTo(getConfiguration());
     }
 
     /**
@@ -334,9 +328,9 @@
      *                    bounds's bottom; false if the task's top should be aligned
      *                    the adjusted bounds's top.
      */
-    void alignToAdjustedBounds(
-            Rect adjustedBounds, Rect tempInsetBounds, boolean alignBottom) {
-        if (!isResizeable() || mOverrideConfig == Configuration.EMPTY) {
+    void alignToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds, boolean alignBottom) {
+        final Configuration overrideConfig = getOverrideConfiguration();
+        if (!isResizeable() || Configuration.EMPTY.equals(overrideConfig)) {
             return;
         }
 
@@ -348,7 +342,7 @@
             mTmpRect2.offsetTo(adjustedBounds.left, adjustedBounds.top);
         }
         setTempInsetBounds(tempInsetBounds);
-        resizeLocked(mTmpRect2, mOverrideConfig, false /* forced */);
+        resizeLocked(mTmpRect2, overrideConfig, false /* forced */);
     }
 
     /** Return true if the current bound can get outputted to the rest of the system as-is. */
@@ -500,12 +494,12 @@
         mTmpRect2.set(mBounds);
 
         if (!StackId.isTaskResizeAllowed(mStack.mStackId)) {
-            setBounds(mTmpRect2, mOverrideConfig);
+            setBounds(mTmpRect2, getOverrideConfiguration());
             return;
         }
 
         displayContent.rotateBounds(mRotation, newRotation, mTmpRect2);
-        if (setBounds(mTmpRect2, mOverrideConfig) != BOUNDS_CHANGE_NONE) {
+        if (setBounds(mTmpRect2, getOverrideConfiguration()) != BOUNDS_CHANGE_NONE) {
             // Post message to inform activity manager of the bounds change simulating a one-way
             // call. We do this to prevent a deadlock between window manager lock and activity
             // manager lock been held.
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 21db840..6887312 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -478,7 +478,7 @@
     private int getDimSide(int x) {
         if (mTask.mStack.mStackId != FREEFORM_WORKSPACE_STACK_ID
                 || !mTask.mStack.fillsParent()
-                || mService.mGlobalConfiguration.orientation != ORIENTATION_LANDSCAPE) {
+                || mTask.mStack.getConfiguration().orientation != ORIENTATION_LANDSCAPE) {
             return CTRL_NONE;
         }
 
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index e374185e..e98fc39 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -230,7 +230,7 @@
             }
         }
         alignTasksToAdjustedBounds(adjusted ? mAdjustedBounds : mBounds, insetBounds);
-        mDisplayContent.layoutNeeded = true;
+        mDisplayContent.setLayoutNeeded();
     }
 
     private void alignTasksToAdjustedBounds(Rect adjustedBounds, Rect tempInsetBounds) {
@@ -354,11 +354,8 @@
         // If the rotation or density didn't match, we'll update it in onConfigurationChanged.
     }
 
-    boolean onConfigurationChanged() {
-        return updateBoundsAfterConfigChange();
-    }
-
-    private boolean updateBoundsAfterConfigChange() {
+    /** @return true if bounds were updated to some non-empty value. */
+    boolean updateBoundsAfterConfigChange() {
         if (mDisplayContent == null) {
             // If the stack is already detached we're not updating anything,
             // as it's going away soon anyway.
@@ -459,7 +456,7 @@
 
         // Snap the position to a target.
         final int rotation = displayInfo.rotation;
-        final int orientation = mService.mGlobalConfiguration.orientation;
+        final int orientation = mDisplayContent.getConfiguration().orientation;
         mService.mPolicy.getStableInsetsLw(rotation, displayWidth, displayHeight, outBounds);
         final DividerSnapAlgorithm algorithm = new DividerSnapAlgorithm(
                 mService.mContext.getResources(), displayWidth, displayHeight,
@@ -598,7 +595,7 @@
             if (mChildren.isEmpty()) {
                 mDisplayContent.moveStack(this, false);
             }
-            mDisplayContent.layoutNeeded = true;
+            mDisplayContent.setLayoutNeeded();
         }
         for (int appNdx = mExitingAppTokens.size() - 1; appNdx >= 0; --appNdx) {
             final AppWindowToken wtoken = mExitingAppTokens.get(appNdx);
@@ -713,7 +710,7 @@
                     di.logicalWidth,
                     di.logicalHeight,
                     dockDividerWidth,
-                    mService.mGlobalConfiguration.orientation == ORIENTATION_PORTRAIT,
+                    mDisplayContent.getConfiguration().orientation == ORIENTATION_PORTRAIT,
                     mTmpRect2).getMiddleTarget().position;
 
             if (dockOnTopOrLeft) {
@@ -1080,7 +1077,7 @@
 
         final Rect insetBounds = mImeGoingAway ? mBounds : mFullyAdjustedImeBounds;
         task.alignToAdjustedBounds(mAdjustedBounds, insetBounds, getDockSide() == DOCKED_TOP);
-        mDisplayContent.layoutNeeded = true;
+        mDisplayContent.setLayoutNeeded();
     }
 
     boolean isAdjustedForMinimizedDockedStack() {
@@ -1182,7 +1179,7 @@
             return DOCKED_INVALID;
         }
         mDisplayContent.getLogicalDisplayRect(mTmpRect);
-        final int orientation = mService.mGlobalConfiguration.orientation;
+        final int orientation = mDisplayContent.getConfiguration().orientation;
         return getDockSideUnchecked(bounds, mTmpRect, orientation);
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index af0fbd3..902f4ae 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import android.annotation.CallSuper;
+import android.content.res.Configuration;
 import android.view.animation.Animation;
 
 import java.util.Comparator;
@@ -34,13 +35,33 @@
  */
 class WindowContainer<E extends WindowContainer> implements Comparable<WindowContainer> {
 
-    // The parent of this window container.
-    protected WindowContainer mParent = null;
+    /**
+     * The parent of this window container.
+     * For removing or setting new parent {@link #setParent} should be used, because it also
+     * performs configuration updates based on new parent's settings.
+     */
+    private WindowContainer mParent = null;
 
     // List of children for this window container. List is in z-order as the children appear on
     // screen with the top-most window container at the tail of the list.
     protected final LinkedList<E> mChildren = new LinkedList();
 
+    /** Contains override configuration settings applied to this window container. */
+    private Configuration mOverrideConfiguration = new Configuration();
+
+    /**
+     * Contains full configuration applied to this window container. Corresponds to full parent's
+     * config with applied {@link #mOverrideConfiguration}.
+     */
+    private Configuration mFullConfiguration = new Configuration();
+
+    /**
+     * Contains merged override configuration settings from the top of the hierarchy down to this
+     * particular instance. It is different from {@link #mFullConfiguration} because it starts from
+     * topmost container's override config instead of global config.
+     */
+    private Configuration mMergedOverrideConfiguration = new Configuration();
+
     // The specified orientation for this window container.
     protected int mOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
 
@@ -48,6 +69,14 @@
         return mParent;
     }
 
+    final protected void setParent(WindowContainer parent) {
+        mParent = parent;
+        // Update full configuration of this container and all its children.
+        onConfigurationChanged(mParent != null ? mParent.mFullConfiguration : Configuration.EMPTY);
+        // Update merged override configuration of this container and all its children.
+        onMergedOverrideConfigurationChanged();
+    }
+
     // Temp. holders for a chain of containers we are currently processing.
     private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList();
     private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList();
@@ -61,12 +90,12 @@
      */
     @CallSuper
     protected void addChild(E child, Comparator<E> comparator) {
-        if (child.mParent != null) {
+        if (child.getParent() != null) {
             throw new IllegalArgumentException("addChild: container=" + child.getName()
-                    + " is already a child of container=" + child.mParent.getName()
+                    + " is already a child of container=" + child.getParent().getName()
                     + " can't add to container=" + getName());
         }
-        child.mParent = this;
+        child.setParent(this);
 
         if (mChildren.isEmpty() || comparator == null) {
             mChildren.add(child);
@@ -87,12 +116,12 @@
     /** Adds the input window container has a child of this container at the input index. */
     @CallSuper
     protected void addChild(E child, int index) {
-        if (child.mParent != null) {
+        if (child.getParent() != null) {
             throw new IllegalArgumentException("addChild: container=" + child.getName()
-                    + " is already a child of container=" + child.mParent.getName()
+                    + " is already a child of container=" + child.getParent().getName()
                     + " can't add to container=" + getName());
         }
-        child.mParent = this;
+        child.setParent(this);
         mChildren.add(index, child);
     }
 
@@ -104,7 +133,7 @@
     @CallSuper
     void removeChild(E child) {
         if (mChildren.remove(child)) {
-            child.mParent = null;
+            child.setParent(null);
         } else {
             throw new IllegalArgumentException("removeChild: container=" + child.getName()
                     + " is not a child of container=" + getName());
@@ -158,6 +187,73 @@
         return false;
     }
 
+    /**
+     * Returns full configuration applied to this window container.
+     * This method should be used for getting settings applied in each particular level of the
+     * hierarchy.
+     */
+    Configuration getConfiguration() {
+        return mFullConfiguration;
+    }
+
+    /**
+     * Notify that parent config changed and we need to update full configuration.
+     * @see #mFullConfiguration
+     */
+    void onConfigurationChanged(Configuration newParentConfig) {
+        mFullConfiguration.setTo(newParentConfig);
+        mFullConfiguration.updateFrom(mOverrideConfiguration);
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer child = mChildren.get(i);
+            child.onConfigurationChanged(mFullConfiguration);
+        }
+    }
+
+    /** Returns override configuration applied to this window container. */
+    Configuration getOverrideConfiguration() {
+        return mOverrideConfiguration;
+    }
+
+    /**
+     * Update override configuration and recalculate full config.
+     * @see #mOverrideConfiguration
+     * @see #mFullConfiguration
+     */
+    void onOverrideConfigurationChanged(Configuration overrideConfiguration) {
+        mOverrideConfiguration.setTo(overrideConfiguration);
+        // Update full configuration of this container and all its children.
+        onConfigurationChanged(mParent != null ? mParent.getConfiguration() : Configuration.EMPTY);
+        // Update merged override config of this container and all its children.
+        onMergedOverrideConfigurationChanged();
+    }
+
+    /**
+     * Get merged override configuration from the top of the hierarchy down to this
+     * particular instance. This should be reported to client as override config.
+     */
+    Configuration getMergedOverrideConfiguration() {
+        return mMergedOverrideConfiguration;
+    }
+
+    /**
+     * Update merged override configuration based on corresponding parent's config and notify all
+     * its children. If there is no parent, merged override configuration will set equal to current
+     * override config.
+     * @see #mMergedOverrideConfiguration
+     */
+    private void onMergedOverrideConfigurationChanged() {
+        if (mParent != null) {
+            mMergedOverrideConfiguration.setTo(mParent.getMergedOverrideConfiguration());
+            mMergedOverrideConfiguration.updateFrom(mOverrideConfiguration);
+        } else {
+            mMergedOverrideConfiguration.setTo(mOverrideConfiguration);
+        }
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer child = mChildren.get(i);
+            child.onMergedOverrideConfigurationChanged();
+        }
+    }
+
     void setWaitingForDrawnIfResizingChanged() {
         for (int i = mChildren.size() - 1; i >= 0; --i) {
             final WindowContainer wc = mChildren.get(i);
@@ -285,19 +381,6 @@
         }
     }
 
-    /**
-     * Updates the current all drawn status for this container. That is all its children
-     * that should draw something have done so.
-     */
-    // TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one
-    // display. Remove once we migrate DisplayContent to use WindowContainer.
-    void updateAllDrawn(int displayId) {
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final WindowContainer wc = mChildren.get(i);
-            wc.updateAllDrawn(displayId);
-        }
-    }
-
     /** Step currently ongoing animation for App window containers. */
     // TODO: The displayId shouldn't be needed as there shouldn't be a container on more than one
     // display. Remove once we migrate DisplayContent to use WindowContainer.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1ed4055..5970998 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -592,12 +592,6 @@
     // State while inside of layoutAndPlaceSurfacesLocked().
     boolean mFocusMayChange;
 
-    /**
-     * Current global configuration information. Contains general settings for the entire system,
-     * corresponds to the configuration of the default display.
-     */
-    Configuration mGlobalConfiguration = new Configuration();
-
     // This is held as long as we have the screen frozen, to give us time to
     // perform a rotation animation when turning off shows the lock screen which
     // changes the orientation.
@@ -2674,7 +2668,8 @@
         // is running.
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WM#applyAnimationLocked");
         if (okToDisplay()) {
-            DisplayInfo displayInfo = getDefaultDisplayInfoLocked();
+            final DisplayContent displayContent = atoken.mTask.getDisplayContent();
+            final DisplayInfo displayInfo = displayContent.getDisplayInfo();
             final int width = displayInfo.appWidth;
             final int height = displayInfo.appHeight;
             if (DEBUG_APP_TRANSITIONS || DEBUG_ANIM) Slog.v(TAG_WM,
@@ -2711,10 +2706,10 @@
             if (DEBUG_APP_TRANSITIONS) Slog.d(TAG_WM, "Loading animation for app transition."
                     + " transit=" + AppTransition.appTransitionToString(transit) + " enter=" + enter
                     + " frame=" + frame + " insets=" + insets + " surfaceInsets=" + surfaceInsets);
-            Animation a = mAppTransition.loadAnimation(lp, transit, enter,
-                    mGlobalConfiguration.uiMode, mGlobalConfiguration.orientation, frame,
-                    displayFrame, insets, surfaceInsets, isVoiceInteraction, freeform,
-                    atoken.mTask.mTaskId);
+            final Configuration displayConfig = displayContent.getConfiguration();
+            Animation a = mAppTransition.loadAnimation(lp, transit, enter, displayConfig.uiMode,
+                    displayConfig.orientation, frame, displayFrame, insets, surfaceInsets,
+                    isVoiceInteraction, freeform, atoken.mTask.mTaskId);
             if (a != null) {
                 if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + atoken);
                 final int containingWidth = frame.width();
@@ -3029,7 +3024,7 @@
             if (currentConfig.diff(mTempConfiguration) != 0) {
                 mWaitingForConfig = true;
                 final DisplayContent displayContent = getDefaultDisplayContentLocked();
-                displayContent.layoutNeeded = true;
+                displayContent.setLayoutNeeded();
                 int anim[] = new int[2];
                 if (displayContent.isDimming()) {
                     anim[0] = anim[1] = 0;
@@ -3104,11 +3099,7 @@
                 mWaitingForConfig = false;
                 mLastFinishedFreezeSource = "new-config";
             }
-            final boolean configChanged = mGlobalConfiguration.diff(config) != 0;
-            if (!configChanged) {
-                return null;
-            }
-            return mRoot.onConfigurationChanged(config);
+            return mRoot.setGlobalConfigurationIfNeeded(config);
         }
     }
 
@@ -3804,7 +3795,7 @@
 
         displayContent.rebuildAppWindowList();
 
-        // Set displayContent.layoutNeeded if window order changed.
+        // Set displayContent.mLayoutNeeded if window order changed.
         final int tmpSize = mTmpWindows.size();
         final int winSize = windows.size();
         int tmpNdx = 0, winNdx = 0;
@@ -3822,13 +3813,13 @@
 
             if (tmp != win) {
                 // Window order changed.
-                displayContent.layoutNeeded = true;
+                displayContent.setLayoutNeeded();
                 break;
             }
         }
         if (tmpNdx != winNdx) {
             // One list was different from the other.
-            displayContent.layoutNeeded = true;
+            displayContent.setLayoutNeeded();
         }
         mTmpWindows.clear();
 
@@ -3992,7 +3983,7 @@
             }
             task.moveTaskToStack(stack, toTop);
             final DisplayContent displayContent = stack.getDisplayContent();
-            displayContent.layoutNeeded = true;
+            displayContent.setLayoutNeeded();
             mWindowPlacerLocked.performSurfacePlacement();
         }
     }
@@ -4044,7 +4035,7 @@
             }
             if (stack.setBounds(bounds, configs, taskBounds, taskTempInsetBounds)
                     && stack.isVisible()) {
-                stack.getDisplayContent().layoutNeeded = true;
+                stack.getDisplayContent().setLayoutNeeded();
                 mWindowPlacerLocked.performSurfacePlacement();
             }
             return stack.getRawFullscreen();
@@ -4081,7 +4072,7 @@
             }
             task.positionTaskInStack(stack, position, bounds, config);
             final DisplayContent displayContent = stack.getDisplayContent();
-            displayContent.layoutNeeded = true;
+            displayContent.setLayoutNeeded();
             mWindowPlacerLocked.performSurfacePlacement();
         }
     }
@@ -4101,7 +4092,7 @@
             }
 
             if (task.resizeLocked(bounds, overrideConfig, forced) && relayout) {
-                task.getDisplayContent().layoutNeeded = true;
+                task.getDisplayContent().setLayoutNeeded();
                 mWindowPlacerLocked.performSurfacePlacement();
             }
         }
@@ -5459,7 +5450,7 @@
         synchronized(mWindowMap) {
             changed = updateRotationUncheckedLocked(false);
             if (!changed || forceRelayout) {
-                getDefaultDisplayContentLocked().layoutNeeded = true;
+                getDefaultDisplayContentLocked().setLayoutNeeded();
                 mWindowPlacerLocked.performSurfacePlacement();
             }
         }
@@ -5542,7 +5533,7 @@
         mH.sendEmptyMessageDelayed(H.WINDOW_FREEZE_TIMEOUT, WINDOW_FREEZE_TIMEOUT_DURATION);
         mWaitingForConfig = true;
         final DisplayContent displayContent = getDefaultDisplayContentLocked();
-        displayContent.layoutNeeded = true;
+        displayContent.setLayoutNeeded();
         final int[] anim = new int[2];
         if (displayContent.isDimming()) {
             anim[0] = anim[1] = 0;
@@ -5595,7 +5586,7 @@
         // the top of the method, the caller is obligated to call computeNewConfigurationLocked().
         // By updating the Display info here it will be available to
         // computeScreenConfigurationLocked later.
-        updateDisplayAndOrientationLocked(mGlobalConfiguration.uiMode);
+        updateDisplayAndOrientationLocked(mRoot.getConfiguration().uiMode);
 
         final DisplayInfo displayInfo = displayContent.getDisplayInfo();
         if (!inTransaction) {
@@ -6961,8 +6952,8 @@
 
                     View view = null;
                     try {
-                        final Configuration overrideConfig = wtoken != null && wtoken.mTask != null
-                                ? wtoken.mTask.mOverrideConfig : null;
+                        final Configuration overrideConfig =
+                                wtoken != null ? wtoken.getMergedOverrideConfiguration() : null;
                         view = mPolicy.addStartingWindow(wtoken.token, sd.pkg, sd.theme,
                             sd.compatInfo, sd.nonLocalizedLabel, sd.labelRes, sd.icon, sd.logo,
                             sd.windowFlags, overrideConfig);
@@ -7908,12 +7899,13 @@
             return;
         }
         configureDisplayPolicyLocked(displayContent);
-        displayContent.layoutNeeded = true;
+        displayContent.setLayoutNeeded();
 
         boolean configChanged = updateOrientationFromAppTokensLocked(false);
-        mTempConfiguration.setTo(mGlobalConfiguration);
+        final Configuration globalConfig = mRoot.getConfiguration();
+        mTempConfiguration.setTo(globalConfig);
         computeScreenConfigurationLocked(mTempConfiguration);
-        configChanged |= mGlobalConfiguration.diff(mTempConfiguration) != 0;
+        configChanged |= globalConfig.diff(mTempConfiguration) != 0;
 
         if (configChanged) {
             mWaitingForConfig = true;
@@ -8056,108 +8048,6 @@
         return changes;
     }
 
-    void updateResizingWindows(final WindowState w) {
-        final WindowStateAnimator winAnimator = w.mWinAnimator;
-        if (w.mHasSurface && w.mLayoutSeq == mLayoutSeq && !w.isGoneForLayoutLw()) {
-            final Task task = w.getTask();
-            // In the case of stack bound animations, the window frames will update (unlike other
-            // animations which just modify various transformation properties). We don't want to
-            // notify the client of frame changes in this case. Not only is it a lot of churn, but
-            // the frame may not correspond to the surface size or the onscreen area at various
-            // phases in the animation, and the client will become sad and confused.
-            if (task != null && task.mStack.getBoundsAnimating()) {
-                return;
-            }
-            w.setReportResizeHints();
-            boolean configChanged = w.isConfigChanged();
-            if (DEBUG_CONFIGURATION && configChanged) {
-                Slog.v(TAG_WM, "Win " + w + " config changed: " + mGlobalConfiguration);
-            }
-            final boolean dragResizingChanged = w.isDragResizeChanged()
-                    && !w.isDragResizingChangeReported();
-
-            if (localLOGV) Slog.v(TAG_WM, "Resizing " + w + ": configChanged=" + configChanged
-                    + " dragResizingChanged=" + dragResizingChanged + " last=" + w.mLastFrame
-                    + " frame=" + w.mFrame);
-
-            // We update mLastFrame always rather than in the conditional with the
-            // last inset variables, because mFrameSizeChanged only tracks the
-            // width and height changing.
-            w.mLastFrame.set(w.mFrame);
-
-            if (w.mContentInsetsChanged
-                    || w.mVisibleInsetsChanged
-                    || winAnimator.mSurfaceResized
-                    || w.mOutsetsChanged
-                    || w.mFrameSizeChanged
-                    || configChanged
-                    || dragResizingChanged
-                    || !w.isResizedWhileNotDragResizingReported()) {
-                if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
-                    Slog.v(TAG_WM, "Resize reasons for w=" + w + ": "
-                            + " contentInsetsChanged=" + w.mContentInsetsChanged
-                            + " " + w.mContentInsets.toShortString()
-                            + " visibleInsetsChanged=" + w.mVisibleInsetsChanged
-                            + " " + w.mVisibleInsets.toShortString()
-                            + " stableInsetsChanged=" + w.mStableInsetsChanged
-                            + " " + w.mStableInsets.toShortString()
-                            + " outsetsChanged=" + w.mOutsetsChanged
-                            + " " + w.mOutsets.toShortString()
-                            + " surfaceResized=" + winAnimator.mSurfaceResized
-                            + " configChanged=" + configChanged
-                            + " dragResizingChanged=" + dragResizingChanged
-                            + " resizedWhileNotDragResizingReported="
-                            + w.isResizedWhileNotDragResizingReported());
-                }
-
-                // If it's a dead window left on screen, and the configuration changed,
-                // there is nothing we can do about it. Remove the window now.
-                if (w.mAppToken != null && w.mAppDied) {
-                    w.mAppToken.removeDeadWindows();
-                    return;
-                }
-
-                w.mLastOverscanInsets.set(w.mOverscanInsets);
-                w.mLastContentInsets.set(w.mContentInsets);
-                w.mLastVisibleInsets.set(w.mVisibleInsets);
-                w.mLastStableInsets.set(w.mStableInsets);
-                w.mLastOutsets.set(w.mOutsets);
-                makeWindowFreezingScreenIfNeededLocked(w);
-                // If the orientation is changing, or we're starting or ending
-                // a drag resizing action, then we need to hold off on unfreezing
-                // the display until this window has been redrawn; to do that,
-                // we need to go through the process of getting informed by the
-                // application when it has finished drawing.
-                if (w.mOrientationChanging || dragResizingChanged
-                        || w.isResizedWhileNotDragResizing()) {
-                    if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION || DEBUG_RESIZE) {
-                        Slog.v(TAG_WM, "Orientation or resize start waiting for draw"
-                                + ", mDrawState=DRAW_PENDING in " + w
-                                + ", surfaceController " + winAnimator.mSurfaceController);
-                    }
-                    winAnimator.mDrawState = DRAW_PENDING;
-                    if (w.mAppToken != null) {
-                        w.mAppToken.clearAllDrawn();
-                    }
-                }
-                if (!mResizingWindows.contains(w)) {
-                    if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                            "Resizing window " + w);
-                    mResizingWindows.add(w);
-                }
-            } else if (w.mOrientationChanging) {
-                if (w.isDrawnLw()) {
-                    if (DEBUG_ORIENTATION) Slog.v(TAG_WM,
-                            "Orientation not waiting for draw in "
-                            + w + ", surfaceController " + winAnimator.mSurfaceController);
-                    w.mOrientationChanging = false;
-                    w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
-                            - mDisplayFreezeTime);
-                }
-            }
-        }
-    }
-
     void checkDrawnWindowsLocked() {
         if (mWaitingForDrawn.isEmpty() || mWaitingForDrawnCallback == null) {
             return;
@@ -8259,7 +8149,7 @@
                     mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
                             && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES);
             if (imWindowChanged) {
-                displayContent.layoutNeeded = true;
+                displayContent.setLayoutNeeded();
                 newFocus = mRoot.computeFocusedWindow();
             }
 
@@ -8286,7 +8176,7 @@
 
             if ((focusChanged & WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT) != 0) {
                 // The change in focus caused us to need to do a layout.  Okay.
-                displayContent.layoutNeeded = true;
+                displayContent.setLayoutNeeded();
                 if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
                     mWindowPlacerLocked.performLayoutLockedInner(displayContent, true /*initial*/,
                             updateInputWindows);
@@ -8959,7 +8849,7 @@
             }
         }
         pw.println();
-        pw.print("  mGlobalConfiguration="); pw.println(mGlobalConfiguration);
+        pw.print("  mGlobalConfiguration="); pw.println(mRoot.getConfiguration());
         pw.print("  mHasPermanentDpad="); pw.println(mHasPermanentDpad);
         pw.print("  mCurrentFocus="); pw.println(mCurrentFocus);
         if (mLastFocus != mCurrentFocus) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index c3affeb..435b1d2 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -87,6 +87,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
@@ -131,6 +132,7 @@
 import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
 import static com.android.server.wm.WindowManagerService.localLOGV;
 import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING;
+import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
 import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
 
@@ -224,14 +226,12 @@
 
     int mLayoutSeq = -1;
 
-    private final Configuration mTmpConfig = new Configuration();
-    // Represents the changes from our override configuration applied
-    // to the global configuration. This is the only form of configuration
-    // which is suitable for delivery to the client.
-    private Configuration mMergedConfiguration = new Configuration();
-    // Sticky answer to isConfigChanged(), remains true until new Configuration is assigned.
-    // Used only on {@link #TYPE_KEYGUARD}.
-    private boolean mConfigHasChanged;
+    /**
+     * Used to store last reported to client configuration and check if we have newer available.
+     * We'll send configuration to client only if it is different from the last applied one and
+     * client won't perform unnecessary updates.
+     */
+    private final Configuration mLastReportedConfiguration = new Configuration();
 
     /**
      * Actual position of the surface shown on-screen (may be modified by animation). These are
@@ -1081,6 +1081,112 @@
                 || mOutsetsChanged || mFrameSizeChanged;
     }
 
+    /**
+     * Adds the window to the resizing list if any of the parameters we use to track the window
+     * dimensions or insets have changed.
+     */
+    void updateResizingWindowIfNeeded() {
+        final WindowStateAnimator winAnimator = mWinAnimator;
+        if (!mHasSurface || mService.mLayoutSeq != mLayoutSeq || isGoneForLayoutLw()) {
+            return;
+        }
+
+        final Task task = getTask();
+        // In the case of stack bound animations, the window frames will update (unlike other
+        // animations which just modify various transformation properties). We don't want to
+        // notify the client of frame changes in this case. Not only is it a lot of churn, but
+        // the frame may not correspond to the surface size or the onscreen area at various
+        // phases in the animation, and the client will become sad and confused.
+        if (task != null && task.mStack.getBoundsAnimating()) {
+            return;
+        }
+
+        setReportResizeHints();
+        boolean configChanged = isConfigChanged();
+        if (DEBUG_CONFIGURATION && configChanged) {
+            Slog.v(TAG_WM, "Win " + this + " config changed: " + getConfiguration());
+        }
+
+        final boolean dragResizingChanged = isDragResizeChanged()
+                && !isDragResizingChangeReported();
+
+        if (localLOGV) Slog.v(TAG_WM, "Resizing " + this + ": configChanged=" + configChanged
+                + " dragResizingChanged=" + dragResizingChanged + " last=" + mLastFrame
+                + " frame=" + mFrame);
+
+        // We update mLastFrame always rather than in the conditional with the last inset
+        // variables, because mFrameSizeChanged only tracks the width and height changing.
+        mLastFrame.set(mFrame);
+
+        if (mContentInsetsChanged
+                || mVisibleInsetsChanged
+                || winAnimator.mSurfaceResized
+                || mOutsetsChanged
+                || mFrameSizeChanged
+                || configChanged
+                || dragResizingChanged
+                || !isResizedWhileNotDragResizingReported()) {
+            if (DEBUG_RESIZE || DEBUG_ORIENTATION) {
+                Slog.v(TAG_WM, "Resize reasons for w=" + this + ": "
+                        + " contentInsetsChanged=" + mContentInsetsChanged
+                        + " " + mContentInsets.toShortString()
+                        + " visibleInsetsChanged=" + mVisibleInsetsChanged
+                        + " " + mVisibleInsets.toShortString()
+                        + " stableInsetsChanged=" + mStableInsetsChanged
+                        + " " + mStableInsets.toShortString()
+                        + " outsetsChanged=" + mOutsetsChanged
+                        + " " + mOutsets.toShortString()
+                        + " surfaceResized=" + winAnimator.mSurfaceResized
+                        + " configChanged=" + configChanged
+                        + " dragResizingChanged=" + dragResizingChanged
+                        + " resizedWhileNotDragResizingReported="
+                        + isResizedWhileNotDragResizingReported());
+            }
+
+            // If it's a dead window left on screen, and the configuration changed, there is nothing
+            // we can do about it. Remove the window now.
+            if (mAppToken != null && mAppDied) {
+                mAppToken.removeDeadWindows();
+                return;
+            }
+
+            mLastOverscanInsets.set(mOverscanInsets);
+            mLastContentInsets.set(mContentInsets);
+            mLastVisibleInsets.set(mVisibleInsets);
+            mLastStableInsets.set(mStableInsets);
+            mLastOutsets.set(mOutsets);
+            mService.makeWindowFreezingScreenIfNeededLocked(this);
+
+            // If the orientation is changing, or we're starting or ending a drag resizing action,
+            // then we need to hold off on unfreezing the display until this window has been
+            // redrawn; to do that, we need to go through the process of getting informed by the
+            // application when it has finished drawing.
+            if (mOrientationChanging || dragResizingChanged || isResizedWhileNotDragResizing()) {
+                if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION || DEBUG_RESIZE) {
+                    Slog.v(TAG_WM, "Orientation or resize start waiting for draw"
+                            + ", mDrawState=DRAW_PENDING in " + this
+                            + ", surfaceController " + winAnimator.mSurfaceController);
+                }
+                winAnimator.mDrawState = DRAW_PENDING;
+                if (mAppToken != null) {
+                    mAppToken.clearAllDrawn();
+                }
+            }
+            if (!mService.mResizingWindows.contains(this)) {
+                if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG_WM, "Resizing window " + this);
+                mService.mResizingWindows.add(this);
+            }
+        } else if (mOrientationChanging) {
+            if (isDrawnLw()) {
+                if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Orientation not waiting for draw in "
+                        + this + ", surfaceController " + winAnimator.mSurfaceController);
+                mOrientationChanging = false;
+                mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
+                        - mService.mDisplayFreezeTime);
+            }
+        }
+    }
+
     public DisplayContent getDisplayContent() {
         if (mAppToken == null || mNotOnAppsDisplay) {
             return mDisplayContent;
@@ -1194,7 +1300,22 @@
 
     @Override
     boolean isVisible() {
-        if ((mAppToken == null || !mAppToken.hiddenRequested) && isVisibleUnchecked()) {
+        // TODO: The check for hiddenRequested is commented out below, because the window can still
+        // be visible on screen when the flag is true. We would like the isVisible() method to
+        // return an answer closer to if the window is truly visible (can't be an exact answer
+        // without checking the surface state), so comment out the check for now so we can test to
+        // see what problem it causes.
+        // If it doesn't cause any issues, then we can remove just before we lock down the current
+        // release (O) and also consolidate this method with #isVisibleUnchecked() and possibly
+        // other methods like isVisibleNow().
+        // If it does cause problems, then we can look if there are other ways to solve the problem.
+        // If there isn't then uncomment and document here why it is needed.
+        if (/*(mAppToken == null || !mAppToken.hiddenRequested) && */isVisibleUnchecked()
+            // TODO: The window isn't considered visible when the token is hidden, however
+            // uncommenting the check below breaks the visual transition from an app to the launcher
+            // if the home buttons is pressed. Need to investigate an fix that issue before
+            // uncommenting.
+            /* && !mToken.hidden*/) {
             // Is this window visible?  It is not visible if there is no surface, or we are in the
             // process of running an exit animation that will remove the surface, or its app token
             // has been hidden.
@@ -1426,7 +1547,7 @@
      * Return true if the window is opaque and fully drawn.  This indicates
      * it may obscure windows behind it.
      */
-    boolean isOpaqueDrawn() {
+    private boolean isOpaqueDrawn() {
         // When there is keyguard, wallpaper could be placed over the secure app
         // window but invisible. We need to check wallpaper visibility explicitly
         // to determine if it's occluding apps.
@@ -1493,7 +1614,7 @@
             }
             changed = true;
             if (displayContent != null) {
-                displayContent.layoutNeeded = true;
+                displayContent.setLayoutNeeded();
             }
         }
 
@@ -1557,10 +1678,49 @@
     }
 
     /**
+     * If the window has moved due to its containing content frame changing, then notify the
+     * listeners and optionally animate it. Simply checking a change of position is not enough,
+     * because being move due to dock divider is not a trigger for animation.
+     */
+    void handleWindowMovedIfNeeded() {
+        if (!hasMoved()) {
+            return;
+        }
+
+        // Frame has moved, containing content frame has also moved, and we're not currently
+        // animating... let's do something.
+        final int left = mFrame.left;
+        final int top = mFrame.top;
+        final Task task = getTask();
+        final boolean adjustedForMinimizedDockOrIme = task != null
+                && (task.mStack.isAdjustedForMinimizedDockedStack()
+                || task.mStack.isAdjustedForIme());
+        if (mService.okToDisplay()
+                && (mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
+                && !isDragResizing() && !adjustedForMinimizedDockOrIme
+                && (task == null || getTask().mStack.hasMovementAnimations())
+                && !mWinAnimator.mLastHidden) {
+            mWinAnimator.setMoveAnimation(left, top);
+        }
+
+        //TODO (multidisplay): Accessibility supported only for the default display.
+        if (mService.mAccessibilityController != null
+                && getDisplayContent().getDisplayId() == Display.DEFAULT_DISPLAY) {
+            mService.mAccessibilityController.onSomeWindowResizedOrMovedLocked();
+        }
+
+        try {
+            mClient.moved(left, top);
+        } catch (RemoteException e) {
+        }
+        mMovedByResize = false;
+    }
+
+    /**
      * Return whether this window has moved. (Only makes
      * sense to call from performLayoutAndPlaceSurfacesLockedInner().)
      */
-    boolean hasMoved() {
+    private boolean hasMoved() {
         return mHasSurface && (mContentChanged || mMovedByResize)
                 && !mAnimatingExit
                 && (mFrame.top != mLastFrame.top || mFrame.left != mLastFrame.left)
@@ -1583,21 +1743,9 @@
                 && mFrame.right >= displayInfo.appWidth && mFrame.bottom >= displayInfo.appHeight;
     }
 
+    /** Returns true if last applied config was not yet requested by client. */
     boolean isConfigChanged() {
-        getMergedConfig(mTmpConfig);
-
-        // If the merged configuration is still empty, it means that we haven't issued the
-        // configuration to the client yet and we need to return true so the configuration updates.
-        boolean configChanged = mMergedConfiguration.equals(Configuration.EMPTY)
-                || mTmpConfig.diff(mMergedConfiguration) != 0;
-
-        if ((mAttrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
-            // Retain configuration changed status until resetConfiguration called.
-            mConfigHasChanged |= configChanged;
-            configChanged = mConfigHasChanged;
-        }
-
-        return configChanged;
+        return !mLastReportedConfiguration.equals(getConfiguration());
     }
 
     boolean isAdjustedForMinimizedDock() {
@@ -2039,7 +2187,7 @@
 
     void setDisplayLayoutNeeded() {
         if (mDisplayContent != null) {
-            mDisplayContent.layoutNeeded = true;
+            mDisplayContent.setLayoutNeeded();
         }
     }
 
@@ -2132,10 +2280,10 @@
             mTurnOnScreen = true;
         }
         if (isConfigChanged()) {
-            final Configuration newConfig = updateConfiguration();
+            outConfig.setTo(getConfiguration());
             if (DEBUG_CONFIGURATION) Slog.i(TAG, "Window " + this + " visible with new config: "
-                    + newConfig);
-            outConfig.setTo(newConfig);
+                    + outConfig);
+            mLastReportedConfiguration.setTo(outConfig);
         }
     }
 
@@ -2503,6 +2651,8 @@
             return unfrozeWindows;
         }
 
+        mAppFreezing = false;
+
         if (mHasSurface && !mOrientationChanging
                 && mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
             if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "set mOrientationChanging of " + this);
@@ -2865,35 +3015,15 @@
         }
     }
 
-    /**
-     * Update our current configurations, based on task configuration.
-     *
-     * @return A configuration suitable for sending to the client.
-     */
-    private Configuration updateConfiguration() {
-        final boolean configChanged = isConfigChanged();
-        getMergedConfig(mMergedConfiguration);
-        mConfigHasChanged = false;
-        if ((DEBUG_RESIZE || DEBUG_ORIENTATION || DEBUG_CONFIGURATION) && configChanged) {
-            Slog.i(TAG, "Sending new config to window " + this + ": " +
-                    " / mergedConfig=" + mMergedConfiguration);
-        }
-        return mMergedConfiguration;
-    }
-
-    private void getMergedConfig(Configuration outConfig) {
+    @Override
+    public Configuration getConfiguration() {
         if (mAppToken != null && mAppToken.mFrozenMergedConfig.size() > 0) {
-            outConfig.setTo(mAppToken.mFrozenMergedConfig.peek());
-            return;
+            return mAppToken.mFrozenMergedConfig.peek();
         }
-        final Task task = getTask();
-        final Configuration overrideConfig = task != null
-                ? task.mOverrideConfig
-                : Configuration.EMPTY;
-        outConfig.setTo(mService.mGlobalConfiguration);
-        if (overrideConfig != Configuration.EMPTY) {
-            outConfig.updateFrom(overrideConfig);
-        }
+
+        // TODO: Remove when all windows' hierarchies will start from same root.
+        return mAppToken != null
+                ? super.getConfiguration() : getDisplayContent().getConfiguration();
     }
 
     void reportResized() {
@@ -2901,7 +3031,13 @@
         try {
             if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG, "Reporting new frame to " + this
                     + ": " + mCompatFrame);
-            final Configuration newConfig = isConfigChanged() ? updateConfiguration() : null;
+            final Configuration newConfig;
+            if (isConfigChanged()) {
+                newConfig = new Configuration(getConfiguration());
+                mLastReportedConfiguration.setTo(newConfig);
+            } else {
+                newConfig = null;
+            }
             if (DEBUG_ORIENTATION && mWinAnimator.mDrawState == WindowStateAnimator.DRAW_PENDING)
                 Slog.i(TAG, "Resizing " + this + " WITH DRAW PENDING");
 
@@ -3223,7 +3359,9 @@
                 getTouchableRegion(region);
                 pw.print(prefix); pw.print("touchable region="); pw.println(region);
             }
-            pw.print(prefix); pw.print("mMergedConfiguration="); pw.println(mMergedConfiguration);
+            pw.print(prefix); pw.print("mFullConfiguration="); pw.println(getConfiguration());
+            pw.print(prefix); pw.print("mLastReportedConfiguration=");
+                    pw.println(mLastReportedConfiguration);
         }
         pw.print(prefix); pw.print("mHasSurface="); pw.print(mHasSurface);
                 pw.print(" mShownPosition="); mShownPosition.printShortString(pw);
@@ -3528,7 +3666,7 @@
     void requestUpdateWallpaperIfNeeded() {
         if (mDisplayContent != null && (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0) {
             mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
-            mDisplayContent.layoutNeeded = true;
+            mDisplayContent.setLayoutNeeded();
             mService.mWindowPlacerLocked.requestTraversal();
         }
 
@@ -3646,7 +3784,7 @@
                     // want to make sure to do a layout.  If called from within the transaction
                     // loop, this will cause it to restart with a new layout.
                     if (displayContent != null) {
-                        displayContent.layoutNeeded = true;
+                        displayContent.setLayoutNeeded();
                     }
                 }
             }
@@ -3928,7 +4066,7 @@
     }
 
     public boolean isRtl() {
-        return mMergedConfiguration.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
+        return getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
     }
 
     void hideWallpaperWindow(boolean wasDeferred, String reason) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index cbb5040..dc83ac0 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -482,7 +482,7 @@
             // Upon completion of a not-visible to visible status bar animation a relayout is
             // required.
             if (displayContent != null) {
-                displayContent.layoutNeeded = true;
+                displayContent.setLayoutNeeded();
             }
         }
 
@@ -966,7 +966,7 @@
             mDtDy = tmpFloats[Matrix.MSCALE_Y];
             float x = tmpFloats[Matrix.MTRANS_X];
             float y = tmpFloats[Matrix.MTRANS_Y];
-            mWin.mShownPosition.set((int) x, (int) y);
+            mWin.mShownPosition.set(Math.round(x), Math.round(y));
 
             // Now set the alpha...  but because our current hardware
             // can't do alpha transformation on a non-opaque surface,
@@ -1059,7 +1059,7 @@
             mDtDy = tmpFloats[Matrix.MSCALE_Y];
             float x = tmpFloats[Matrix.MTRANS_X];
             float y = tmpFloats[Matrix.MTRANS_Y];
-            mWin.mShownPosition.set((int) x, (int) y);
+            mWin.mShownPosition.set(Math.round(x), Math.round(y));
 
             mShownAlpha = mAlpha;
         } else {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 6d10c5a..668e1b4 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -25,6 +25,7 @@
 import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
 
+import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.PixelFormat;
@@ -168,7 +169,7 @@
 
             mInLayout = false;
 
-            if (mService.mRoot.layoutNeeded()) {
+            if (mService.mRoot.isLayoutNeeded()) {
                 if (++mLayoutRepeatCount < 6) {
                     requestTraversal();
                 } else {
@@ -204,10 +205,10 @@
 
     final void performLayoutLockedInner(final DisplayContent displayContent,
             boolean initial, boolean updateInputWindows) {
-        if (!displayContent.layoutNeeded) {
+        if (!displayContent.isLayoutNeeded()) {
             return;
         }
-        displayContent.layoutNeeded = false;
+        displayContent.clearLayoutNeeded();
         WindowList windows = displayContent.getWindowList();
         boolean isDefaultDisplay = displayContent.isDefaultDisplay;
 
@@ -228,12 +229,12 @@
 
         if (DEBUG_LAYOUT) {
             Slog.v(TAG, "-------------------------------------");
-            Slog.v(TAG, "performLayout: needed="
-                    + displayContent.layoutNeeded + " dw=" + dw + " dh=" + dh);
+            Slog.v(TAG, "performLayout: needed=" + displayContent.isLayoutNeeded()
+                    + " dw=" + dw + " dh=" + dh);
         }
 
         mService.mPolicy.beginLayoutLw(isDefaultDisplay, dw, dh, mService.mRotation,
-                mService.mGlobalConfiguration.uiMode);
+                displayContent.getConfiguration().uiMode);
         if (isDefaultDisplay) {
             // Not needed on non-default displays.
             mService.mSystemDecorLayer = mService.mPolicy.getSystemDecorLayerLw();
@@ -427,7 +428,7 @@
         if ((displayContent.pendingLayoutChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0 &&
                 mWallpaperControllerLocked.adjustWallpaperWindows()) {
             mService.mLayersController.assignLayersLocked(windows);
-            displayContent.layoutNeeded = true;
+            displayContent.setLayoutNeeded();
         }
 
         final WindowState lowerWallpaperTarget =
@@ -532,7 +533,7 @@
 
         // This has changed the visibility of windows, so perform
         // a new layout to get them all up-to-date.
-        displayContent.layoutNeeded = true;
+        displayContent.setLayoutNeeded();
 
         // TODO(multidisplay): IMEs are only supported on the default display.
         if (windows == mService.getDefaultWindowListLocked()
@@ -840,13 +841,14 @@
                 Rect appRect = win != null ? win.getContentFrameLw() :
                         new Rect(0, 0, displayInfo.appWidth, displayInfo.appHeight);
                 Rect insets = win != null ? win.mContentInsets : null;
+                final Configuration displayConfig = displayContent.getConfiguration();
                 // For the new aspect-scaled transition, we want it to always show
                 // above the animating opening/closing window, and we want to
                 // synchronize its thumbnail surface with the surface for the
                 // open/close animation (only on the way down)
                 anim = mService.mAppTransition.createThumbnailAspectScaleAnimationLocked(appRect,
-                        insets, thumbnailHeader, taskId, mService.mGlobalConfiguration.uiMode,
-                        mService.mGlobalConfiguration.orientation);
+                        insets, thumbnailHeader, taskId, displayConfig.uiMode,
+                        displayConfig.orientation);
                 openingAppAnimator.thumbnailForceAboveLayer = Math.max(openingLayer, closingLayer);
                 openingAppAnimator.deferThumbnailDestruction =
                         !mService.mAppTransition.isNextThumbnailTransitionScaleUp();
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 7ed8e78..177652c 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import android.annotation.CallSuper;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.IBinder;
@@ -258,7 +257,7 @@
         if (hidden == visible) {
             hidden = !visible;
             // Need to do a layout to ensure the wallpaper now has the correct size.
-            displayContent.layoutNeeded = true;
+            displayContent.setLayoutNeeded();
         }
 
         final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
@@ -281,7 +280,7 @@
                     "Wallpaper token " + token + " hidden=" + !visible);
             hidden = !visible;
             // Need to do a layout to ensure the wallpaper now has the correct size.
-            mService.getDefaultDisplayContentLocked().layoutNeeded = true;
+            mService.getDefaultDisplayContentLocked().setLayoutNeeded();
         }
 
         final WallpaperController wallpaperController = mService.mWallpaperControllerLocked;
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index aba4dc0..769b5ee9 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -859,12 +859,7 @@
 
             if (!disableNonCoreServices) {
                 traceBeginAndSlog("StartClipboardService");
-                try {
-                    ServiceManager.addService(Context.CLIPBOARD_SERVICE,
-                            new ClipboardService(context));
-                } catch (Throwable e) {
-                    reportWtf("starting Clipboard Service", e);
-                }
+                mSystemServiceManager.startService(ClipboardService.class);
                 traceEnd();
             }
 
@@ -1372,12 +1367,9 @@
         mmsService = mSystemServiceManager.startService(MmsServiceBroker.class);
         traceEnd();
 
-        if (Settings.Global.getInt(mContentResolver, Settings.Global.DEVICE_PROVISIONED, 0) == 0 ||
-                UserManager.isDeviceInDemoMode(mSystemContext)) {
-            traceBeginAndSlog("StartRetailDemoModeService");
-            mSystemServiceManager.startService(RetailDemoModeService.class);
-            traceEnd();
-        }
+        traceBeginAndSlog("StartRetailDemoModeService");
+        mSystemServiceManager.startService(RetailDemoModeService.class);
+        traceEnd();
 
         // It is now time to start up the app processes...
 
diff --git a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
index f97e557..7c7c299 100644
--- a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
+++ b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
@@ -124,7 +124,12 @@
     @GuardedBy("mActivityLock")
     long mLastUserActivityTime;
 
-    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+    private boolean mSafeBootRestrictionInitialState;
+    private int mPackageVerifierEnableInitialState;
+
+    private IntentReceiver mBroadcastReceiver = null;
+
+    private final class IntentReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
             if (!mDeviceInDemoMode) {
@@ -150,6 +155,9 @@
 
         @Override
         public void handleMessage(Message msg) {
+            if (!mDeviceInDemoMode) {
+                return;
+            }
             switch (msg.what) {
                 case MSG_TURN_SCREEN_ON:
                     if (mInjector.isWakeLockHeld()) {
@@ -219,7 +227,7 @@
             if (mDeviceDemoModeUri.equals(uri)) {
                 mDeviceInDemoMode = UserManager.isDeviceInDemoMode(getContext());
                 if (mDeviceInDemoMode) {
-                    putDeviceInDemoMode();
+                    startDemoMode();
                 } else {
                     mInjector.systemPropertiesSet(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, "0");
                     if (mInjector.isWakeLockHeld()) {
@@ -238,6 +246,7 @@
                         }
                     }
                 });
+                stopDemoMode();
             }
         }
 
@@ -376,10 +385,20 @@
     }
 
     private void registerBroadcastReceiver() {
-        final IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_SCREEN_OFF);
-        filter.addAction(ACTION_RESET_DEMO);
-        getContext().registerReceiver(mBroadcastReceiver, filter);
+        if (mBroadcastReceiver == null) {
+            final IntentFilter filter = new IntentFilter();
+            filter.addAction(Intent.ACTION_SCREEN_OFF);
+            filter.addAction(ACTION_RESET_DEMO);
+            mBroadcastReceiver = new IntentReceiver();
+            getContext().registerReceiver(mBroadcastReceiver, filter);
+        }
+    }
+
+    private void unregisterBroadcastReceiver() {
+        if (mBroadcastReceiver != null) {
+            getContext().unregisterReceiver(mBroadcastReceiver);
+            mBroadcastReceiver = null;
+        }
     }
 
     private String[] getCameraIdsWithFlash() {
@@ -407,9 +426,33 @@
         }
     }
 
-    private void putDeviceInDemoMode() {
+    private void startDemoMode() {
+        mPreloadAppsInstaller = mInjector.getPreloadAppsInstaller();
+        mInjector.initializeWakeLock();
+        if (mCameraIdsWithFlash == null) {
+            mCameraIdsWithFlash = getCameraIdsWithFlash();
+        }
+        registerBroadcastReceiver();
+
         mInjector.systemPropertiesSet(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, "1");
         mHandler.sendEmptyMessage(MSG_START_NEW_SESSION);
+
+        mSafeBootRestrictionInitialState = mInjector.getUserManager().hasUserRestriction(
+                UserManager.DISALLOW_SAFE_BOOT, UserHandle.SYSTEM);
+        mPackageVerifierEnableInitialState = Settings.Global.getInt(mInjector.getContentResolver(),
+                Settings.Global.PACKAGE_VERIFIER_ENABLE, 0);
+    }
+
+    private void stopDemoMode() {
+        mPreloadAppsInstaller = null;
+        mCameraIdsWithFlash = null;
+        mInjector.destroyWakeLock();
+        unregisterBroadcastReceiver();
+
+        mInjector.getUserManager().setUserRestriction(UserManager.DISALLOW_SAFE_BOOT,
+                mSafeBootRestrictionInitialState, UserHandle.SYSTEM);
+        Settings.Global.putInt(mInjector.getContentResolver(),
+                Settings.Global.PACKAGE_VERIFIER_ENABLE, mPackageVerifierEnableInitialState);
     }
 
     @Override
@@ -421,25 +464,21 @@
                 false);
         mHandlerThread.start();
         mHandler = new MainHandler(mHandlerThread.getLooper());
-        publishLocalService(RetailDemoModeServiceInternal.class, mLocalService);
+        mInjector.publishLocalService(this, mLocalService);
     }
 
     @Override
     public void onBootPhase(int bootPhase) {
         switch (bootPhase) {
             case PHASE_THIRD_PARTY_APPS_CAN_START:
-                mPreloadAppsInstaller = mInjector.getPreloadAppsInstaller();
-                mInjector.initializeWakeLock();
-                mCameraIdsWithFlash = getCameraIdsWithFlash();
                 SettingsObserver settingsObserver = new SettingsObserver(mHandler);
                 settingsObserver.register();
                 settingsObserver.refreshTimeoutConstants();
-                registerBroadcastReceiver();
                 break;
             case PHASE_BOOT_COMPLETED:
                 if (UserManager.isDeviceInDemoMode(getContext())) {
                     mDeviceInDemoMode = true;
-                    putDeviceInDemoMode();
+                    startDemoMode();
                 }
                 break;
         }
@@ -466,8 +505,8 @@
                 mInjector.getSystemUsersConfiguration(), userId);
         mInjector.turnOffAllFlashLights(mCameraIdsWithFlash);
         muteVolumeStreams();
-        if (!mInjector.isWifiEnabled()) {
-            mInjector.enableWifi();
+        if (!mInjector.getWifiManager().isWifiEnabled()) {
+            mInjector.getWifiManager().setWifiEnabled(true);
         }
         // Disable lock screen for demo users.
         mInjector.getLockPatternUtils().setLockScreenDisabled(true, userId);
@@ -526,6 +565,7 @@
         private WifiManager mWifiManager;
         private Configuration mSystemUserConfiguration;
         private PendingIntent mResetDemoPendingIntent;
+        private PreloadAppsInstaller mPreloadAppsInstaller;
 
         Injector(Context context) {
             mContext = context;
@@ -535,7 +575,7 @@
             return mContext;
         }
 
-        private WifiManager getWifiManager() {
+        WifiManager getWifiManager() {
             if (mWifiManager == null) {
                 mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
             }
@@ -609,7 +649,10 @@
         }
 
         PreloadAppsInstaller getPreloadAppsInstaller() {
-            return new PreloadAppsInstaller(getContext());
+            if (mPreloadAppsInstaller == null) {
+                mPreloadAppsInstaller = new PreloadAppsInstaller(getContext());
+            }
+            return mPreloadAppsInstaller;
         }
 
         void systemPropertiesSet(String key, String value) {
@@ -628,8 +671,14 @@
         }
 
         void initializeWakeLock() {
-            mWakeLock = getPowerManager().newWakeLock(
-                    PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG);
+            if (mWakeLock == null) {
+                mWakeLock = getPowerManager().newWakeLock(
+                        PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, TAG);
+            }
+        }
+
+        void destroyWakeLock() {
+            mWakeLock = null;
         }
 
         boolean isWakeLockHeld() {
@@ -644,14 +693,6 @@
             mWakeLock.release();
         }
 
-        boolean isWifiEnabled() {
-            return getWifiManager().isWifiEnabled();
-        }
-
-        void enableWifi() {
-            getWifiManager().setWifiEnabled(true);
-        }
-
         void logSessionDuration(int duration) {
             MetricsLogger.histogram(getContext(), DEMO_SESSION_DURATION, duration);
         }
@@ -696,5 +737,10 @@
         File getDataPreloadsDirectory() {
             return Environment.getDataPreloadsDirectory();
         }
+
+        void publishLocalService(RetailDemoModeService service,
+                RetailDemoModeServiceInternal localService) {
+            service.publishLocalService(RetailDemoModeServiceInternal.class, localService);
+        }
     }
 }
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index b76392c..3f5b96e 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -53,6 +53,8 @@
 LOCAL_JACK_FLAGS := --multi-dex native
 endif # EMMA_INSTRUMENT_STATIC
 
+LOCAL_STATIC_JAVA_LIBRARIES += ub-uiautomator
+
 include $(BUILD_PACKAGE)
 
 #########################################################################
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index b8ace28..514f095 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -43,6 +43,7 @@
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
     <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT" />
+    <uses-permission android:name="android.permission.MANAGE_ACTIVITY_STACKS" />
 
     <application>
         <uses-library android:name="android.test.runner" />
@@ -155,6 +156,9 @@
             </intent-filter>
         </activity-alias>
 
+        <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityA" />
+        <activity android:name="com.android.server.am.TaskStackChangedListenerTest$ActivityB" />
+
     </application>
 
     <instrumentation
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
new file mode 100644
index 0000000..5b565a7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountsDbTest.java
@@ -0,0 +1,341 @@
+
+/*
+ * 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.server.accounts;
+
+import android.accounts.Account;
+import android.content.Context;
+import android.database.Cursor;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Pair;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests for {@link AccountsDb}.
+ * <p>Run with:<pre>
+ * m FrameworksServicesTests &&
+ * adb install \
+ * -r out/target/product/marlin/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ * adb shell am instrument -e class com.android.server.accounts.AccountsDbTest \
+ * -w com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner
+ * </pre>
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class AccountsDbTest {
+    private static final String PREN_DB = "pren.db";
+    private static final String DE_DB = "de.db";
+    private static final String CE_DB = "ce.db";
+
+    private AccountsDb mAccountsDb;
+    private File preNDb;
+    private File deDb;
+    private File ceDb;
+
+    @Before
+    public void setUp() {
+        Context context = InstrumentationRegistry.getContext();
+        preNDb = new File(context.getCacheDir(), PREN_DB);
+        ceDb = new File(context.getCacheDir(), CE_DB);
+        deDb = new File(context.getCacheDir(), DE_DB);
+        deleteDbFiles();
+        mAccountsDb = AccountsDb.create(context, 0, preNDb, deDb);
+    }
+
+    @After
+    public void tearDown() {
+        deleteDbFiles();
+    }
+
+    private void deleteDbFiles() {
+        AccountsDb.deleteDbFileWarnIfFailed(preNDb);
+        AccountsDb.deleteDbFileWarnIfFailed(ceDb);
+        AccountsDb.deleteDbFileWarnIfFailed(deDb);
+    }
+
+    @Test
+    public void testCeNotAvailableInitially() {
+        Account account = new Account("name", "example.com");
+        long id = mAccountsDb.insertCeAccount(account, "");
+        assertEquals("Insert into CE should fail until CE database is attached", -1, id);
+    }
+
+    @Test
+    public void testDeAccountInsertFindDelete() {
+        Account account = new Account("name", "example.com");
+        long accId = 1;
+        mAccountsDb.insertDeAccount(account, accId);
+        long actualId = mAccountsDb.findDeAccountId(account);
+        assertEquals(accId, actualId);
+        // Delete and verify that account no longer exists
+        mAccountsDb.deleteDeAccount(accId);
+        actualId = mAccountsDb.findDeAccountId(account);
+        assertEquals(-1, actualId);
+    }
+
+    @Test
+    public void testCeAccountInsertFindDelete() {
+        mAccountsDb.attachCeDatabase(ceDb);
+        Account account = new Account("name", "example.com");
+        long accId = mAccountsDb.insertCeAccount(account, "password");
+        long actualId = mAccountsDb.findCeAccountId(account);
+        assertEquals(accId, actualId);
+        // Delete and verify that account no longer exists
+        mAccountsDb.deleteCeAccount(accId);
+        actualId = mAccountsDb.findCeAccountId(account);
+        assertEquals(-1, actualId);
+    }
+
+    @Test
+    public void testAuthTokenInsertFindDelete() {
+        mAccountsDb.attachCeDatabase(ceDb);
+        Account account = new Account("name", "example.com");
+        long accId = mAccountsDb.insertCeAccount(account, "password");
+        mAccountsDb.insertDeAccount(account, accId);
+        long authTokenId = mAccountsDb.insertAuthToken(accId, "type", "token");
+        Map<String, String> authTokensByAccount = mAccountsDb.findAuthTokensByAccount(account);
+        assertEquals(1, authTokensByAccount.size());
+        try (Cursor cursor = mAccountsDb.findAuthtokenForAllAccounts(account.type, "token")) {
+            assertTrue(cursor.moveToNext());
+        }
+        try (Cursor cursor = mAccountsDb.findAuthtokenForAllAccounts(account.type, "nosuchtoken")) {
+            assertFalse(cursor.moveToNext());
+        }
+        mAccountsDb.deleteAuthToken(String.valueOf(authTokenId));
+        // Verify that token no longer exists
+        authTokensByAccount = mAccountsDb.findAuthTokensByAccount(account);
+        assertEquals(0, authTokensByAccount.size());
+    }
+
+    @Test
+    public void testAuthTokenDeletes() {
+        mAccountsDb.attachCeDatabase(ceDb);
+        // 1st account
+        Account account = new Account("name", "example.com");
+        long accId = mAccountsDb.insertCeAccount(account, "password");
+        mAccountsDb.insertDeAccount(account, accId);
+        mAccountsDb.insertAuthToken(accId, "type", "token");
+        mAccountsDb.insertAuthToken(accId, "type2", "token2");
+        // 2nd account
+        Account account2 = new Account("name", "example2.com");
+        long accId2 = mAccountsDb.insertCeAccount(account2, "password");
+        mAccountsDb.insertDeAccount(account2, accId);
+        mAccountsDb.insertAuthToken(accId2, "type", "token");
+
+        mAccountsDb.deleteAuthTokensByAccountId(accId2);
+        Map<String, String> authTokensByAccount = mAccountsDb.findAuthTokensByAccount(account2);
+        assertEquals(0, authTokensByAccount.size());
+        // Authtokens from account 1 are still there
+        authTokensByAccount = mAccountsDb.findAuthTokensByAccount(account);
+        assertEquals(2, authTokensByAccount.size());
+
+        // Delete authtokens from account 1 and verify
+        mAccountsDb.deleteAuthtokensByAccountIdAndType(accId, "type");
+        authTokensByAccount = mAccountsDb.findAuthTokensByAccount(account);
+        assertEquals(1, authTokensByAccount.size());
+        mAccountsDb.deleteAuthtokensByAccountIdAndType(accId, "type2");
+        authTokensByAccount = mAccountsDb.findAuthTokensByAccount(account);
+        assertEquals(0, authTokensByAccount.size());
+    }
+
+    @Test
+    public void testExtrasInsertFindDelete() {
+        mAccountsDb.attachCeDatabase(ceDb);
+        Account account = new Account("name", "example.com");
+        long accId = mAccountsDb.insertCeAccount(account, "password");
+        mAccountsDb.insertDeAccount(account, accId);
+        String extraKey = "extra_key";
+        String extraValue = "extra_value";
+        long extraId = mAccountsDb.insertExtra(accId, extraKey, extraValue);
+        // Test find methods
+        long actualExtraId = mAccountsDb.findExtrasIdByAccountId(accId, extraKey);
+        assertEquals(extraId, actualExtraId);
+        Map<String, String> extras = mAccountsDb.findUserExtrasForAccount(account);
+        assertEquals(1, extras.size());
+        assertEquals(extraValue, extras.get(extraKey));
+        // Test update
+        String newExtraValue = "extra_value2";
+        mAccountsDb.updateExtra(extraId, newExtraValue);
+        String newValue = mAccountsDb.findUserExtrasForAccount(account).get(extraKey);
+        assertEquals(newExtraValue, newValue);
+
+        // Delete account and verify that extras cascade removed
+        mAccountsDb.deleteCeAccount(accId);
+        actualExtraId = mAccountsDb.findExtrasIdByAccountId(accId, extraKey);
+        assertEquals(-1, actualExtraId);
+    }
+
+    @Test
+    public void testGrantsInsertFindDelete() {
+        mAccountsDb.attachCeDatabase(ceDb);
+        Account account = new Account("name", "example.com");
+        long accId = mAccountsDb.insertCeAccount(account, "password");
+        mAccountsDb.insertDeAccount(account, accId);
+        int testUid = 100500;
+        long grantId = mAccountsDb.insertGrant(accId, "tokenType", testUid);
+        assertTrue(grantId > 0);
+        List<Integer> allUidGrants = mAccountsDb.findAllUidGrants();
+        List<Integer> expectedUids = Arrays.asList(testUid);
+        assertEquals(expectedUids, allUidGrants);
+
+        long matchingGrantsCount = mAccountsDb.findMatchingGrantsCount(
+                testUid, "tokenType", account);
+        assertEquals(1, matchingGrantsCount);
+        // Test nonexistent type
+        matchingGrantsCount = mAccountsDb.findMatchingGrantsCount(
+                testUid, "noSuchType", account);
+        assertEquals(0, matchingGrantsCount);
+
+        matchingGrantsCount = mAccountsDb.findMatchingGrantsCountAnyToken(testUid, account);
+        assertEquals(1, matchingGrantsCount);
+
+        List<Pair<String, Integer>> allAccountGrants = mAccountsDb.findAllAccountGrants();
+        assertEquals(1, allAccountGrants.size());
+        assertEquals(account.name, allAccountGrants.get(0).first);
+        assertEquals(testUid, (int)allAccountGrants.get(0).second);
+
+        mAccountsDb.deleteGrantsByUid(testUid);
+        allUidGrants = mAccountsDb.findAllUidGrants();
+        assertTrue("Test grants should be removed", allUidGrants.isEmpty());
+    }
+
+    @Test
+    public void testSharedAccountsInsertFindDelete() {
+        Account account = new Account("name", "example.com");
+        long accId = 0;
+        mAccountsDb.insertDeAccount(account, accId);
+        long sharedAccId = mAccountsDb.insertSharedAccount(account);
+        long foundSharedAccountId = mAccountsDb.findSharedAccountId(account);
+        assertEquals(sharedAccId, foundSharedAccountId);
+        List<Account> sharedAccounts = mAccountsDb.getSharedAccounts();
+        List<Account> expectedList = Arrays.asList(account);
+        assertEquals(expectedList, sharedAccounts);
+
+        // Delete and verify
+        mAccountsDb.deleteSharedAccount(account);
+        foundSharedAccountId = mAccountsDb.findSharedAccountId(account);
+        assertEquals(-1, foundSharedAccountId);
+    }
+
+    @Test
+    public void testMetaInsertFindDelete() {
+        int testUid = 100500;
+        String authenticatorType = "authType";
+        mAccountsDb.insertOrReplaceMetaAuthTypeAndUid(authenticatorType, testUid);
+        Map<String, Integer> metaAuthUid = mAccountsDb.findMetaAuthUid();
+        assertEquals(1, metaAuthUid.size());
+        assertEquals(testUid, (int)metaAuthUid.get(authenticatorType));
+
+        // Delete and verify
+        boolean deleteResult = mAccountsDb.deleteMetaByAuthTypeAndUid(authenticatorType, testUid);
+        assertTrue(deleteResult);
+        metaAuthUid = mAccountsDb.findMetaAuthUid();
+        assertEquals(0, metaAuthUid.size());
+    }
+
+    @Test
+    public void testUpdateDeAccountLastAuthenticatedTime() {
+        Account account = new Account("name", "example.com");
+        long accId = 1;
+        mAccountsDb.insertDeAccount(account, accId);
+        long now = System.currentTimeMillis();
+        mAccountsDb.updateAccountLastAuthenticatedTime(account);
+        long time = mAccountsDb.findAccountLastAuthenticatedTime(account);
+        assertTrue("LastAuthenticatedTime should be current", time >= now);
+    }
+
+    @Test
+    public void testRenameAccount() {
+        mAccountsDb.attachCeDatabase(ceDb);
+        Account account = new Account("name", "example.com");
+        long accId = mAccountsDb.insertCeAccount(account, "password");
+        mAccountsDb.insertDeAccount(account, accId);
+        mAccountsDb.renameDeAccount(accId, "newName", "name");
+        Account newAccount = mAccountsDb.findAllDeAccounts().get(accId);
+        assertEquals("newName", newAccount.name);
+
+        String prevName = mAccountsDb.findDeAccountPreviousName(newAccount);
+        assertEquals("name", prevName);
+        mAccountsDb.renameCeAccount(accId, "newName");
+        long foundAccId = mAccountsDb.findCeAccountId(account);
+        assertEquals("Account shouldn't be found under the old name", -1, foundAccId);
+        foundAccId = mAccountsDb.findCeAccountId(newAccount);
+        assertEquals(accId, foundAccId);
+    }
+
+    @Test
+    public void testUpdateCeAccountPassword() {
+        mAccountsDb.attachCeDatabase(ceDb);
+        Account account = new Account("name", "example.com");
+        long accId = mAccountsDb.insertCeAccount(account, "password");
+        String newPassword = "newPassword";
+        mAccountsDb.updateCeAccountPassword(accId, newPassword);
+        String actualPassword = mAccountsDb
+                .findAccountPasswordByNameAndType(account.name, account.type);
+        assertEquals(newPassword, actualPassword);
+    }
+
+    @Test
+    public void testFindCeAccountsNotInDe() {
+        mAccountsDb.attachCeDatabase(ceDb);
+        Account account = new Account("name", "example.com");
+        long accId = mAccountsDb.insertCeAccount(account, "password");
+        mAccountsDb.insertDeAccount(account, accId);
+
+        Account accountNotInDe = new Account("name2", "example.com");
+        mAccountsDb.insertCeAccount(accountNotInDe, "password");
+
+        List<Account> ceAccounts = mAccountsDb.findCeAccountsNotInDe();
+        List<Account> expectedList = Arrays.asList(accountNotInDe);
+        assertEquals(expectedList, ceAccounts);
+    }
+
+    @Test
+    public void testCrossDbTransactions() {
+        mAccountsDb.attachCeDatabase(ceDb);
+        mAccountsDb.beginTransaction();
+        Account account = new Account("name", "example.com");
+        long accId;
+        accId = mAccountsDb.insertCeAccount(account, "password");
+        accId = mAccountsDb.insertDeAccount(account, accId);
+        long actualId = mAccountsDb.findCeAccountId(account);
+        assertEquals(accId, actualId);
+        actualId = mAccountsDb.findDeAccountId(account);
+        assertEquals(accId, actualId);
+        mAccountsDb.endTransaction();
+        // Verify that records were removed
+        actualId = mAccountsDb.findCeAccountId(account);
+        assertEquals(-1, actualId);
+        actualId = mAccountsDb.findDeAccountId(account);
+        assertEquals(-1, actualId);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java b/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
new file mode 100644
index 0000000..b47d17e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/TaskStackChangedListenerTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.server.am;
+
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+
+import android.app.Activity;
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.app.ITaskStackListener;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.support.test.uiautomator.UiDevice;
+
+import com.android.internal.annotations.GuardedBy;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class TaskStackChangedListenerTest extends ITaskStackListener.Stub {
+
+    private IActivityManager mService;
+
+    private static final Object sLock = new Object();
+    @GuardedBy("sLock")
+    private static boolean sTaskStackChangedCalled;
+    private static boolean sActivityBResumed;
+
+    @Before
+    public void setUp() throws Exception {
+        mService = ActivityManagerNative.getDefault();
+        mService.registerTaskStackListener(this);
+    }
+
+    @Test
+    public void testTaskStackChanged_afterFinish() throws Exception {
+        Context ctx = InstrumentationRegistry.getContext();
+        ctx.startActivity(new Intent(ctx, ActivityA.class));
+        UiDevice.getInstance(getInstrumentation()).waitForIdle();
+        synchronized (sLock) {
+            Assert.assertTrue(sTaskStackChangedCalled);
+        }
+        Assert.assertTrue(sActivityBResumed);
+    }
+
+    @Override
+    public void onTaskStackChanged() throws RemoteException {
+        synchronized (sLock) {
+            sTaskStackChangedCalled = true;
+        }
+    }
+
+    @Override
+    public void onActivityPinned() throws RemoteException {
+    }
+
+    @Override
+    public void onPinnedActivityRestartAttempt() throws RemoteException {
+    }
+
+    @Override
+    public void onPinnedStackAnimationEnded() throws RemoteException {
+    }
+
+    @Override
+    public void onActivityForcedResizable(String packageName, int taskId) throws RemoteException {
+    }
+
+    @Override
+    public void onActivityDismissingDockedStack() throws RemoteException {
+    }
+
+    public static class ActivityA extends Activity {
+
+        private boolean mActivityBLaunched = false;
+
+        @Override
+        protected void onPostResume() {
+            super.onPostResume();
+            if (mActivityBLaunched) {
+                return;
+            }
+            mActivityBLaunched = true;
+            finish();
+            startActivity(new Intent(this, ActivityB.class));
+        }
+    }
+
+    public static class ActivityB extends Activity {
+
+        @Override
+        protected void onPostResume() {
+            super.onPostResume();
+            synchronized (sLock) {
+                sTaskStackChangedCalled = false;
+            }
+            sActivityBResumed = true;
+            finish();
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
index 3c99174..25f9100 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest7.java
@@ -68,7 +68,7 @@
                     /* fdin*/ null,
                     /* fdout*/ fd.getFileDescriptor(),
                     /* fderr*/ fd.getFileDescriptor(),
-                        args, rr);
+                        args, null, rr);
             }
             return readAll(out);
         } finally {
diff --git a/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java b/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java
index 56a170c..e4473ad 100644
--- a/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java
@@ -46,6 +46,7 @@
 import android.content.res.Configuration;
 import android.media.AudioManager;
 import android.net.Uri;
+import android.net.wifi.WifiManager;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.Looper;
@@ -62,7 +63,6 @@
 
 import com.android.internal.util.FakeSettingsProvider;
 import com.android.internal.widget.LockPatternUtils;
-import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.retaildemo.RetailDemoModeService.Injector;
 
@@ -75,7 +75,6 @@
 import org.mockito.ArgumentCaptor;
 import org.mockito.ArgumentMatcher;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.io.File;
@@ -97,6 +96,7 @@
     private @Mock NotificationManager mNm;
     private @Mock ActivityManagerInternal mAmi;
     private @Mock AudioManager mAudioManager;
+    private @Mock WifiManager mWifiManager;
     private @Mock LockPatternUtils mLockPatternUtils;
     private MockPreloadAppsInstaller mPreloadAppsInstaller;
     private MockContentResolver mContentResolver;
@@ -135,9 +135,6 @@
 
     @After
     public void tearDown() {
-        // Remove the RetailDemoModeServiceInternal from LocalServices which would've been
-        // added during initialization of RetailDemoModeService in setUp().
-        LocalServices.removeServiceForTest(RetailDemoModeServiceInternal.class);
         FileUtils.deleteContentsAndDir(mTestPreloadsDir);
     }
 
@@ -145,23 +142,22 @@
     public void testDemoUserSetup() throws Exception {
         mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
 
+        mLatch = new CountDownLatch(1);
+        final UserInfo userInfo = new UserInfo();
+        userInfo.id = TEST_DEMO_USER;
+        when(mUm.createUser(anyString(), anyInt())).thenReturn(userInfo);
+
+        setCameraPackage(TEST_CAMERA_PKG);
+        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+        assertEquals(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED + " property not set",
+                "1", mInjector.systemPropertiesGet(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED));
+
         final ArgumentCaptor<IntentFilter> intentFilter =
                 ArgumentCaptor.forClass(IntentFilter.class);
         verify(mContext).registerReceiver(any(BroadcastReceiver.class), intentFilter.capture());
         assertTrue("Not registered for " + Intent.ACTION_SCREEN_OFF,
                 intentFilter.getValue().hasAction(Intent.ACTION_SCREEN_OFF));
 
-        mLatch = new CountDownLatch(1);
-
-        mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
-        assertEquals(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED + " property not set",
-                "1", mInjector.systemPropertiesGet(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED));
-
-        final UserInfo userInfo = new UserInfo();
-        userInfo.id = TEST_DEMO_USER;
-        when(mUm.createUser(anyString(), anyInt())).thenReturn(userInfo);
-        mInjector.setDemoUserId(TEST_DEMO_USER);
-        setCameraPackage(TEST_CAMERA_PKG);
         // Wait for the setup to complete.
         mLatch.await(SETUP_COMPLETE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
         ArgumentCaptor<Integer> flags = ArgumentCaptor.forClass(Integer.class);
@@ -197,7 +193,36 @@
     }
 
     @Test
-    public void testSettingsObserver() throws Exception {
+    public void testSettingsObserver_disableDemoMode() throws Exception {
+        final RetailDemoModeService.SettingsObserver observer =
+                mService.new SettingsObserver(new Handler(Looper.getMainLooper()));
+        final Uri deviceDemoModeUri = Settings.Global.getUriFor(Settings.Global.DEVICE_DEMO_MODE);
+        when(mUm.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT, UserHandle.SYSTEM))
+                .thenReturn(false);
+        Settings.Global.putInt(mContentResolver, Settings.Global.PACKAGE_VERIFIER_ENABLE, 1);
+        // Settings.Global.DEVICE_DEMO_MODE has been set to 1 initially.
+        observer.onChange(false, deviceDemoModeUri);
+        final ArgumentCaptor<BroadcastReceiver> receiver =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        verify(mContext).registerReceiver(receiver.capture(), any(IntentFilter.class));
+
+        Settings.Global.putInt(mContentResolver, Settings.Global.PACKAGE_VERIFIER_ENABLE, 0);
+        new File(mTestPreloadsDir, "dir1").mkdirs();
+        new File(mTestPreloadsDir, "file1").createNewFile();
+        Settings.Global.putInt(mContentResolver, Settings.Global.DEVICE_DEMO_MODE, 0);
+        observer.onChange(false, deviceDemoModeUri);
+        verify(mContext).unregisterReceiver(receiver.getValue());
+        verify(mUm).setUserRestriction(UserManager.DISALLOW_SAFE_BOOT, false, UserHandle.SYSTEM);
+        assertEquals("Package verifier enable value has not been reset", 1,
+                Settings.Global.getInt(mContentResolver, Settings.Global.PACKAGE_VERIFIER_ENABLE));
+        Thread.sleep(20); // Wait for the deletion to complete.
+        // verify that the preloaded directory is emptied.
+        assertEquals("Preloads directory is not emptied",
+                0, mTestPreloadsDir.list().length);
+    }
+
+    @Test
+    public void testSettingsObserver_enableDemoMode() throws Exception {
         final RetailDemoModeService.SettingsObserver observer =
                 mService.new SettingsObserver(new Handler(Looper.getMainLooper()));
         final Uri deviceDemoModeUri = Settings.Global.getUriFor(Settings.Global.DEVICE_DEMO_MODE);
@@ -206,14 +231,11 @@
         assertEquals(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED + " property not set",
                 "1", mInjector.systemPropertiesGet(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED));
 
-        new File(mTestPreloadsDir, "dir1").mkdirs();
-        new File(mTestPreloadsDir, "file1").createNewFile();
-        Settings.Global.putInt(mContentResolver, Settings.Global.DEVICE_DEMO_MODE, 0);
-        observer.onChange(false, deviceDemoModeUri);
-        Thread.sleep(20); // Wait for the deletion to complete.
-        // verify that the preloaded directory is emptied.
-        assertEquals("Preloads directory is not emptied",
-                0, mTestPreloadsDir.list().length);
+        final ArgumentCaptor<IntentFilter> intentFilter =
+                ArgumentCaptor.forClass(IntentFilter.class);
+        verify(mContext).registerReceiver(any(BroadcastReceiver.class), intentFilter.capture());
+        assertTrue("Not registered for " + Intent.ACTION_SCREEN_OFF,
+                intentFilter.getValue().hasAction(Intent.ACTION_SCREEN_OFF));
     }
 
     @Test
@@ -227,6 +249,7 @@
         final UserInfo userInfo = new UserInfo(TEST_DEMO_USER, "demo_user",
                 UserInfo.FLAG_DEMO | UserInfo.FLAG_EPHEMERAL);
         when(mUm.getUserInfo(TEST_DEMO_USER)).thenReturn(userInfo);
+        when(mWifiManager.isWifiEnabled()).thenReturn(false);
         final int minVolume = -111;
         for (int stream : RetailDemoModeService.VOLUME_STREAMS_TO_MUTE) {
             when(mAudioManager.getStreamMinVolume(stream)).thenReturn(minVolume);
@@ -238,6 +261,7 @@
             verify(mAudioManager).setStreamVolume(stream, minVolume, 0);
         }
         verify(mLockPatternUtils).setLockScreenDisabled(true, TEST_DEMO_USER);
+        verify(mWifiManager).setWifiEnabled(true);
     }
 
     private void setCameraPackage(String pkgName) {
@@ -304,7 +328,6 @@
 
     private class TestInjector extends Injector {
         private ArrayMap<String, String> mSystemProperties = new ArrayMap<>();
-        private int mDemoUserId = UserHandle.USER_NULL;
 
         TestInjector() {
             super(mContext);
@@ -321,6 +344,11 @@
         }
 
         @Override
+        WifiManager getWifiManager() {
+            return mWifiManager;
+        }
+
+        @Override
         void switchUser(int userId) {
             if (mLatch != null) {
                 mLatch.countDown();
@@ -376,6 +404,10 @@
         }
 
         @Override
+        void destroyWakeLock() {
+        }
+
+        @Override
         boolean isWakeLockHeld() {
             return false;
         }
@@ -416,12 +448,13 @@
             return mTestPreloadsDir;
         }
 
-        String systemPropertiesGet(String key) {
-            return mSystemProperties.get(key);
+        @Override
+        void publishLocalService(RetailDemoModeService service,
+                RetailDemoModeServiceInternal localService) {
         }
 
-        void setDemoUserId(int userId) {
-            mDemoUserId = userId;
+        String systemPropertiesGet(String key) {
+            return mSystemProperties.get(key);
         }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
index eb2372a..6eb347b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowContainerTests.java
@@ -19,6 +19,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import android.content.res.Configuration;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -28,6 +29,8 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static org.junit.Assert.assertEquals;
@@ -377,6 +380,160 @@
         assertEquals(1, child2223.compareTo(child21));
     }
 
+    @Test
+    public void testConfigurationInit() throws Exception {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+
+        // Check root container initial config.
+        final TestWindowContainer root = builder.setLayer(0).build();
+        assertEquals(Configuration.EMPTY, root.getOverrideConfiguration());
+        assertEquals(Configuration.EMPTY, root.getMergedOverrideConfiguration());
+        assertEquals(Configuration.EMPTY, root.getConfiguration());
+
+        // Check child initial config.
+        final TestWindowContainer child1 = root.addChildWindow();
+        assertEquals(Configuration.EMPTY, child1.getOverrideConfiguration());
+        assertEquals(Configuration.EMPTY, child1.getMergedOverrideConfiguration());
+        assertEquals(Configuration.EMPTY, child1.getConfiguration());
+
+        // Check child initial config if root has overrides.
+        final Configuration rootOverrideConfig = new Configuration();
+        rootOverrideConfig.fontScale = 1.3f;
+        root.onOverrideConfigurationChanged(rootOverrideConfig);
+        final TestWindowContainer child2 = root.addChildWindow();
+        assertEquals(Configuration.EMPTY, child2.getOverrideConfiguration());
+        assertEquals(rootOverrideConfig, child2.getMergedOverrideConfiguration());
+        assertEquals(rootOverrideConfig, child2.getConfiguration());
+
+        // Check child initial config if root has parent config set.
+        final Configuration rootParentConfig = new Configuration();
+        rootParentConfig.fontScale = 0.8f;
+        rootParentConfig.orientation = SCREEN_ORIENTATION_LANDSCAPE;
+        root.onConfigurationChanged(rootParentConfig);
+        final Configuration rootFullConfig = new Configuration(rootParentConfig);
+        rootFullConfig.updateFrom(rootOverrideConfig);
+
+        final TestWindowContainer child3 = root.addChildWindow();
+        assertEquals(Configuration.EMPTY, child3.getOverrideConfiguration());
+        assertEquals(rootOverrideConfig, child3.getMergedOverrideConfiguration());
+        assertEquals(rootFullConfig, child3.getConfiguration());
+    }
+
+    @Test
+    public void testConfigurationChangeOnAddRemove() throws Exception {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+
+        // Init root's config.
+        final TestWindowContainer root = builder.setLayer(0).build();
+        final Configuration rootOverrideConfig = new Configuration();
+        rootOverrideConfig.fontScale = 1.3f;
+        root.onOverrideConfigurationChanged(rootOverrideConfig);
+
+        // Init child's config.
+        final TestWindowContainer child = root.addChildWindow();
+        final Configuration childOverrideConfig = new Configuration();
+        childOverrideConfig.densityDpi = 320;
+        child.onOverrideConfigurationChanged(childOverrideConfig);
+
+        // Check configuration update when child is removed from parent.
+        root.removeChild(child);
+        assertEquals(childOverrideConfig, child.getOverrideConfiguration());
+        assertEquals(childOverrideConfig, child.getMergedOverrideConfiguration());
+        assertEquals(childOverrideConfig, child.getConfiguration());
+
+        // It may be paranoia... but let's check if parent's config didn't change after removal.
+        assertEquals(rootOverrideConfig, root.getOverrideConfiguration());
+        assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration());
+        assertEquals(rootOverrideConfig, root.getConfiguration());
+
+        // Check configuration update when child is added to parent.
+        final Configuration mergedOverrideConfig = new Configuration(root.getConfiguration());
+        mergedOverrideConfig.updateFrom(childOverrideConfig);
+        root.addChildWindow(child);
+        assertEquals(childOverrideConfig, child.getOverrideConfiguration());
+        assertEquals(mergedOverrideConfig, child.getMergedOverrideConfiguration());
+        assertEquals(mergedOverrideConfig, child.getConfiguration());
+    }
+
+    @Test
+    public void testConfigurationChangePropagation() throws Exception {
+        final TestWindowContainerBuilder builder = new TestWindowContainerBuilder();
+
+        // Builds 3-level vertical hierarchy with one window container on each level.
+        // In addition to different overrides on each level, everyone in hierarchy will have one
+        // common overridden value - orientation;
+
+        // Init root's config.
+        final TestWindowContainer root = builder.setLayer(0).build();
+        final Configuration rootOverrideConfig = new Configuration();
+        rootOverrideConfig.fontScale = 1.3f;
+        rootOverrideConfig.orientation = SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
+        root.onOverrideConfigurationChanged(rootOverrideConfig);
+
+        // Init children.
+        final TestWindowContainer child1 = root.addChildWindow();
+        final Configuration childOverrideConfig1 = new Configuration();
+        childOverrideConfig1.densityDpi = 320;
+        childOverrideConfig1.orientation = SCREEN_ORIENTATION_LANDSCAPE;
+        child1.onOverrideConfigurationChanged(childOverrideConfig1);
+
+        final TestWindowContainer child2 = child1.addChildWindow();
+        final Configuration childOverrideConfig2 = new Configuration();
+        childOverrideConfig2.screenWidthDp = 150;
+        childOverrideConfig2.orientation = SCREEN_ORIENTATION_PORTRAIT;
+        child2.onOverrideConfigurationChanged(childOverrideConfig2);
+
+        // Check configuration on all levels when root override is updated.
+        rootOverrideConfig.smallestScreenWidthDp = 200;
+        root.onOverrideConfigurationChanged(rootOverrideConfig);
+
+        final Configuration mergedOverrideConfig1 = new Configuration(rootOverrideConfig);
+        mergedOverrideConfig1.updateFrom(childOverrideConfig1);
+        final Configuration mergedConfig1 = new Configuration(mergedOverrideConfig1);
+
+        final Configuration mergedOverrideConfig2 = new Configuration(mergedOverrideConfig1);
+        mergedOverrideConfig2.updateFrom(childOverrideConfig2);
+        final Configuration mergedConfig2 = new Configuration(mergedOverrideConfig2);
+
+        assertEquals(rootOverrideConfig, root.getOverrideConfiguration());
+        assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration());
+        assertEquals(rootOverrideConfig, root.getConfiguration());
+
+        assertEquals(childOverrideConfig1, child1.getOverrideConfiguration());
+        assertEquals(mergedOverrideConfig1, child1.getMergedOverrideConfiguration());
+        assertEquals(mergedConfig1, child1.getConfiguration());
+
+        assertEquals(childOverrideConfig2, child2.getOverrideConfiguration());
+        assertEquals(mergedOverrideConfig2, child2.getMergedOverrideConfiguration());
+        assertEquals(mergedConfig2, child2.getConfiguration());
+
+        // Check configuration on all levels when root parent config is updated.
+        final Configuration rootParentConfig = new Configuration();
+        rootParentConfig.screenHeightDp = 100;
+        rootParentConfig.orientation = SCREEN_ORIENTATION_REVERSE_PORTRAIT;
+        root.onConfigurationChanged(rootParentConfig);
+        final Configuration mergedRootConfig = new Configuration(rootParentConfig);
+        mergedRootConfig.updateFrom(rootOverrideConfig);
+
+        mergedConfig1.setTo(mergedRootConfig);
+        mergedConfig1.updateFrom(mergedOverrideConfig1);
+
+        mergedConfig2.setTo(mergedConfig1);
+        mergedConfig2.updateFrom(mergedOverrideConfig2);
+
+        assertEquals(rootOverrideConfig, root.getOverrideConfiguration());
+        assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration());
+        assertEquals(mergedRootConfig, root.getConfiguration());
+
+        assertEquals(childOverrideConfig1, child1.getOverrideConfiguration());
+        assertEquals(mergedOverrideConfig1, child1.getMergedOverrideConfiguration());
+        assertEquals(mergedConfig1, child1.getConfiguration());
+
+        assertEquals(childOverrideConfig2, child2.getOverrideConfiguration());
+        assertEquals(mergedOverrideConfig2, child2.getMergedOverrideConfiguration());
+        assertEquals(mergedConfig2, child2.getConfiguration());
+    }
+
     /* Used so we can gain access to some protected members of the {@link WindowContainer} class */
     private class TestWindowContainer extends WindowContainer<TestWindowContainer> {
         private final int mLayer;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ad5b9d1..deed6d4 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -182,6 +182,13 @@
     public static final String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
 
     /**
+     * Since the default voicemail number is empty, if a SIM card does not have a voicemail number
+     * available the user cannot use voicemail. This flag allows the user to edit the voicemail
+     * number in such cases, and is false by default.
+     */
+    public static final String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL= "editable_voicemail_number_bool";
+
+    /**
      * Determine whether the voicemail notification is persistent in the notification bar. If true,
      * the voicemail notifications cannot be dismissed from the notification bar.
      */
@@ -1084,6 +1091,7 @@
         sDefaults.putBoolean(KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL, false);
         sDefaults.putBoolean(KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL, true);
         sDefaults.putBoolean(KEY_USE_HFA_FOR_PROVISIONING_BOOL, false);
+        sDefaults.putBoolean(KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL, false);
         sDefaults.putBoolean(KEY_USE_OTASP_FOR_PROVISIONING_BOOL, false);
         sDefaults.putBoolean(KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL, false);
         sDefaults.putBoolean(KEY_VOICE_PRIVACY_DISABLE_UI_BOOL, false);
diff --git a/tests/VoiceInteraction/AndroidManifest.xml b/tests/VoiceInteraction/AndroidManifest.xml
index cbc6c76..5fdf0dd 100644
--- a/tests/VoiceInteraction/AndroidManifest.xml
+++ b/tests/VoiceInteraction/AndroidManifest.xml
@@ -1,6 +1,8 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.test.voiceinteraction">
 
+    <uses-sdk android:minSdkVersion="23" android:targetSdkVersion="25" />
+
     <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
     <uses-permission android:name="android.permission.READ_LOGS" />
 
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index fb1370e..9b62e14 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -1281,7 +1281,7 @@
                         const size_t NL = locales.size();
                         for (size_t i=0; i<NL; i++) {
                             const char* localeStr =  locales[i].string();
-                            assets.setLocale(localeStr != NULL ? localeStr : "");
+                            assets.setConfiguration(config, localeStr != NULL ? localeStr : "");
                             String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR,
                                     &error);
                             if (llabel != "") {
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 60114fb..b57d4db 100644
--- a/tools/aapt2/Android.mk
+++ b/tools/aapt2/Android.mk
@@ -24,7 +24,10 @@
 sources := \
 	compile/IdAssigner.cpp \
 	compile/InlineXmlFormatParser.cpp \
+	compile/NinePatch.cpp \
 	compile/Png.cpp \
+	compile/PngChunkFilter.cpp \
+	compile/PngCrunch.cpp \
 	compile/PseudolocaleGenerator.cpp \
 	compile/Pseudolocalizer.cpp \
 	compile/XmlIdCollector.cpp \
@@ -34,12 +37,14 @@
 	flatten/XmlFlattener.cpp \
 	io/File.cpp \
 	io/FileSystem.cpp \
+	io/Io.cpp \
 	io/ZipArchive.cpp \
 	link/AutoVersioner.cpp \
 	link/ManifestFixer.cpp \
 	link/ProductFilter.cpp \
 	link/PrivateAttributeMover.cpp \
 	link/ReferenceLinker.cpp \
+	link/ResourceDeduper.cpp \
 	link/TableMerger.cpp \
 	link/VersionCollapser.cpp \
 	link/XmlNamespaceRemover.cpp \
@@ -56,6 +61,7 @@
 	util/Util.cpp \
 	ConfigDescription.cpp \
 	Debug.cpp \
+	DominatorTree.cpp \
 	Flags.cpp \
 	java/AnnotationProcessor.cpp \
 	java/ClassDefinition.cpp \
@@ -82,6 +88,7 @@
 testSources := \
 	compile/IdAssigner_test.cpp \
 	compile/InlineXmlFormatParser_test.cpp \
+	compile/NinePatch_test.cpp \
 	compile/PseudolocaleGenerator_test.cpp \
 	compile/Pseudolocalizer_test.cpp \
 	compile/XmlIdCollector_test.cpp \
@@ -93,6 +100,7 @@
 	link/PrivateAttributeMover_test.cpp \
 	link/ProductFilter_test.cpp \
 	link/ReferenceLinker_test.cpp \
+	link/ResourceDeduper_test.cpp \
 	link/TableMerger_test.cpp \
 	link/VersionCollapser_test.cpp \
 	link/XmlNamespaceRemover_test.cpp \
@@ -106,6 +114,7 @@
 	util/StringPiece_test.cpp \
 	util/Util_test.cpp \
 	ConfigDescription_test.cpp \
+	DominatorTree_test.cpp \
 	java/AnnotationProcessor_test.cpp \
 	java/JavaClassGenerator_test.cpp \
 	java/ManifestClassGenerator_test.cpp \
diff --git a/tools/aapt2/ConfigDescription.cpp b/tools/aapt2/ConfigDescription.cpp
index c1697e7..1812d96 100644
--- a/tools/aapt2/ConfigDescription.cpp
+++ b/tools/aapt2/ConfigDescription.cpp
@@ -789,4 +789,96 @@
     return copy;
 }
 
+bool ConfigDescription::dominates(const ConfigDescription& o) const {
+    if (*this == defaultConfig() || *this == o) {
+        return true;
+    }
+    return matchWithDensity(o)
+            && !o.matchWithDensity(*this)
+            && !isMoreSpecificThan(o)
+            && !o.hasHigherPrecedenceThan(*this);
+}
+
+bool ConfigDescription::hasHigherPrecedenceThan(const ConfigDescription& o) const {
+    // The order of the following tests defines the importance of one
+    // configuration parameter over another. Those tests first are more
+    // important, trumping any values in those following them.
+    // The ordering should be the same as ResTable_config#isBetterThan.
+    if (mcc || o.mcc) return (!o.mcc);
+    if (mnc || o.mnc) return (!o.mnc);
+    if (language[0] || o.language[0]) return (!o.language[0]);
+    if (country[0] || o.country[0]) return (!o.country[0]);
+    // Script and variant require either a language or country, both of which
+    // have higher precedence.
+    if ((screenLayout | o.screenLayout) & MASK_LAYOUTDIR) {
+        return !(o.screenLayout & MASK_LAYOUTDIR);
+    }
+    if (smallestScreenWidthDp || o.smallestScreenWidthDp) return (!o.smallestScreenWidthDp);
+    if (screenWidthDp || o.screenWidthDp) return (!o.screenWidthDp);
+    if (screenHeightDp || o.screenHeightDp) return (!o.screenHeightDp);
+    if ((screenLayout | o.screenLayout) & MASK_SCREENSIZE) {
+        return !(o.screenLayout & MASK_SCREENSIZE);
+    }
+    if ((screenLayout | o.screenLayout) & MASK_SCREENLONG) {
+        return !(o.screenLayout & MASK_SCREENLONG);
+    }
+    if ((screenLayout2 | o.screenLayout2) & MASK_SCREENROUND) {
+        return !(o.screenLayout2 & MASK_SCREENROUND);
+    }
+    if (orientation || o.orientation) return (!o.orientation);
+    if ((uiMode | o.uiMode) & MASK_UI_MODE_TYPE) {
+        return !(o.uiMode & MASK_UI_MODE_TYPE);
+    }
+    if ((uiMode | o.uiMode) & MASK_UI_MODE_NIGHT) {
+        return !(o.uiMode & MASK_UI_MODE_NIGHT);
+    }
+    if (density || o.density) return (!o.density);
+    if (touchscreen || o.touchscreen) return (!o.touchscreen);
+    if ((inputFlags | o.inputFlags) & MASK_KEYSHIDDEN) {
+        return !(o.inputFlags & MASK_KEYSHIDDEN);
+    }
+    if ((inputFlags | o.inputFlags) & MASK_NAVHIDDEN) {
+        return !(o.inputFlags & MASK_NAVHIDDEN);
+    }
+    if (keyboard || o.keyboard) return (!o.keyboard);
+    if (navigation || o.navigation) return (!o.navigation);
+    if (screenWidth || o.screenWidth) return (!o.screenWidth);
+    if (screenHeight || o.screenHeight) return (!o.screenHeight);
+    if (sdkVersion || o.sdkVersion) return (!o.sdkVersion);
+    if (minorVersion || o.minorVersion) return (!o.minorVersion);
+    // Both configurations have nothing defined except some possible future
+    // value. Returning the comparison of the two configurations is a
+    // "best effort" at this point to protect against incorrect dominations.
+    return *this != o;
+}
+
+bool ConfigDescription::conflictsWith(const ConfigDescription& o) const {
+    // This method should be updated as new configuration parameters are
+    // introduced (e.g. screenConfig2).
+    auto pred = [](const uint32_t a, const uint32_t b) -> bool {
+        return a == 0 || b == 0 || a == b;
+    };
+    // The values here can be found in ResTable_config#match. Density and range
+    // values can't lead to conflicts, and are ignored.
+    return !pred(mcc, o.mcc)
+            || !pred(mnc, o.mnc)
+            || !pred(locale, o.locale)
+            || !pred(screenLayout & MASK_LAYOUTDIR, o.screenLayout & MASK_LAYOUTDIR)
+            || !pred(screenLayout & MASK_SCREENLONG, o.screenLayout & MASK_SCREENLONG)
+            || !pred(screenLayout & MASK_UI_MODE_TYPE, o.screenLayout & MASK_UI_MODE_TYPE)
+            || !pred(uiMode & MASK_UI_MODE_TYPE, o.uiMode & MASK_UI_MODE_TYPE)
+            || !pred(uiMode & MASK_UI_MODE_NIGHT, o.uiMode & MASK_UI_MODE_NIGHT)
+            || !pred(screenLayout2 & MASK_SCREENROUND, o.screenLayout2 & MASK_SCREENROUND)
+            || !pred(orientation, o.orientation)
+            || !pred(touchscreen, o.touchscreen)
+            || !pred(inputFlags & MASK_KEYSHIDDEN, o.inputFlags & MASK_KEYSHIDDEN)
+            || !pred(inputFlags & MASK_NAVHIDDEN, o.inputFlags & MASK_NAVHIDDEN)
+            || !pred(keyboard, o.keyboard)
+            || !pred(navigation, o.navigation);
+}
+
+bool ConfigDescription::isCompatibleWith(const ConfigDescription& o) const {
+    return !conflictsWith(o) && !dominates(o) && !o.dominates(*this);
+}
+
 } // namespace aapt
diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h
index 6858c62..d801621 100644
--- a/tools/aapt2/ConfigDescription.h
+++ b/tools/aapt2/ConfigDescription.h
@@ -59,14 +59,50 @@
     ConfigDescription& operator=(const ConfigDescription& o);
     ConfigDescription& operator=(ConfigDescription&& o);
 
+    ConfigDescription copyWithoutSdkVersion() const;
+
+    /**
+     * A configuration X dominates another configuration Y, if X has at least the
+     * precedence of Y and X is strictly more general than Y: for any type defined
+     * by X, the same type is defined by Y with a value equal to or, in the case
+     * of ranges, more specific than that of X.
+     *
+     * For example, the configuration 'en-w800dp' dominates 'en-rGB-w1024dp'. It
+     * does not dominate 'fr', 'en-w720dp', or 'mcc001-en-w800dp'.
+     */
+    bool dominates(const ConfigDescription& o) const;
+
+    /**
+     * Returns true if this configuration defines a more important configuration
+     * parameter than o. For example, "en" has higher precedence than "v23",
+     * whereas "en" has the same precedence as "en-v23".
+     */
+    bool hasHigherPrecedenceThan(const ConfigDescription& o) const;
+
+    /**
+     * A configuration conflicts with another configuration if both
+     * configurations define an incompatible configuration parameter. An
+     * incompatible configuration parameter is a non-range, non-density parameter
+     * that is defined in both configurations as a different, non-default value.
+     */
+    bool conflictsWith(const ConfigDescription& o) const;
+
+    /**
+     * A configuration is compatible with another configuration if both
+     * configurations can match a common concrete device configuration and are
+     * unrelated by domination. For example, land-v11 conflicts with port-v21
+     * but is compatible with v21 (both land-v11 and v21 would match en-land-v23).
+     */
+    bool isCompatibleWith(const ConfigDescription& o) const;
+
+    bool matchWithDensity(const ConfigDescription& o) const;
+
     bool operator<(const ConfigDescription& o) const;
     bool operator<=(const ConfigDescription& o) const;
     bool operator==(const ConfigDescription& o) const;
     bool operator!=(const ConfigDescription& o) const;
     bool operator>=(const ConfigDescription& o) const;
     bool operator>(const ConfigDescription& o) const;
-
-    ConfigDescription copyWithoutSdkVersion() const;
 };
 
 inline ConfigDescription::ConfigDescription() {
@@ -103,6 +139,10 @@
     return *this;
 }
 
+inline bool ConfigDescription::matchWithDensity(const ConfigDescription& o) const {
+    return match(o) && (density == 0 || density == o.density);
+}
+
 inline bool ConfigDescription::operator<(const ConfigDescription& o) const {
     return compare(o) < 0;
 }
diff --git a/tools/aapt2/DominatorTree.cpp b/tools/aapt2/DominatorTree.cpp
new file mode 100644
index 0000000..29587a8
--- /dev/null
+++ b/tools/aapt2/DominatorTree.cpp
@@ -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.
+ */
+
+#include "ConfigDescription.h"
+#include "DominatorTree.h"
+
+#include <algorithm>
+
+namespace aapt {
+
+DominatorTree::DominatorTree(
+        const std::vector<std::unique_ptr<ResourceConfigValue>>& configs) {
+    for (const auto& config : configs) {
+        mProductRoots[config->product].tryAddChild(
+                util::make_unique<Node>(config.get(), nullptr));
+    }
+}
+
+void DominatorTree::accept(Visitor* visitor) {
+    for (auto& entry : mProductRoots) {
+        visitor->visitTree(entry.first, &entry.second);
+    }
+}
+
+bool DominatorTree::Node::tryAddChild(std::unique_ptr<Node> newChild) {
+    assert(newChild->mValue && "cannot add a root or empty node as a child");
+    if (mValue && !dominates(newChild.get())) {
+        // This is not the root and the child dominates us.
+        return false;
+    }
+    return addChild(std::move(newChild));
+}
+
+bool DominatorTree::Node::addChild(std::unique_ptr<Node> newChild) {
+    bool hasDominatedChildren = false;
+    // Demote children dominated by the new config.
+    for (auto& child : mChildren) {
+        if (newChild->dominates(child.get())) {
+            child->mParent = newChild.get();
+            newChild->mChildren.push_back(std::move(child));
+            child = {};
+            hasDominatedChildren = true;
+        }
+    }
+    // Remove dominated children.
+    if (hasDominatedChildren) {
+        mChildren.erase(std::remove_if(mChildren.begin(), mChildren.end(),
+                [](const std::unique_ptr<Node>& child) -> bool {
+            return child == nullptr;
+        }), mChildren.end());
+    }
+    // Add the new config to a child if a child dominates the new config.
+    for (auto& child : mChildren) {
+        if (child->dominates(newChild.get())) {
+            child->addChild(std::move(newChild));
+            return true;
+        }
+    }
+    // The new config is not dominated by a child, so add it here.
+    newChild->mParent = this;
+    mChildren.push_back(std::move(newChild));
+    return true;
+}
+
+bool DominatorTree::Node::dominates(const Node* other) const {
+    // Check root node dominations.
+    if (other->isRootNode()) {
+        return isRootNode();
+    } else if (isRootNode()) {
+        return true;
+    }
+    // Neither node is a root node; compare the configurations.
+    return mValue->config.dominates(other->mValue->config);
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/DominatorTree.h b/tools/aapt2/DominatorTree.h
new file mode 100644
index 0000000..ad2df0e
--- /dev/null
+++ b/tools/aapt2/DominatorTree.h
@@ -0,0 +1,127 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_DOMINATOR_TREE_H
+#define AAPT_DOMINATOR_TREE_H
+
+#include "ResourceTable.h"
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+/**
+ * A dominator tree of configurations as defined by resolution rules for Android
+ * resources.
+ *
+ * A node in the tree represents a resource configuration.
+ *
+ * The tree has the following property:
+ *
+ * Each child of a given configuration defines a strict superset of qualifiers
+ * and has a value that is at least as specific as that of its ancestors. A
+ * value is "at least as specific" if it is either identical or it represents a
+ * stronger requirement.
+ * For example, v21 is more specific than v11, and w1200dp is more specific than
+ * w800dp.
+ *
+ * The dominator tree relies on the underlying configurations passed to it. If
+ * the configurations passed to the dominator tree go out of scope, the tree
+ * will exhibit undefined behavior.
+ */
+class DominatorTree {
+public:
+    explicit DominatorTree(const std::vector<std::unique_ptr<ResourceConfigValue>>& configs);
+
+    class Node {
+    public:
+        explicit Node(ResourceConfigValue* value = nullptr, Node* parent = nullptr) :
+                mValue(value), mParent(parent) {
+        }
+
+        inline ResourceConfigValue* value() const {
+            return mValue;
+        }
+
+        inline Node* parent() const {
+            return mParent;
+        }
+
+        inline bool isRootNode() const {
+            return !mValue;
+        }
+
+        inline const std::vector<std::unique_ptr<Node>>& children() const {
+            return mChildren;
+        }
+
+        bool tryAddChild(std::unique_ptr<Node> newChild);
+
+    private:
+        bool addChild(std::unique_ptr<Node> newChild);
+        bool dominates(const Node* other) const;
+
+        ResourceConfigValue* mValue;
+        Node* mParent;
+        std::vector<std::unique_ptr<Node>> mChildren;
+
+        DISALLOW_COPY_AND_ASSIGN(Node);
+    };
+
+    struct Visitor {
+        virtual ~Visitor() = default;
+        virtual void visitTree(const std::string& product, Node* root) = 0;
+    };
+
+    class BottomUpVisitor : public Visitor {
+    public:
+        virtual ~BottomUpVisitor() = default;
+
+        void visitTree(const std::string& product, Node* root) override {
+            for (auto& child : root->children()) {
+                visitNode(child.get());
+            }
+        }
+
+        virtual void visitConfig(Node* node) = 0;
+
+    private:
+        void visitNode(Node* node) {
+            for (auto& child : node->children()) {
+                visitNode(child.get());
+            }
+            visitConfig(node);
+        }
+    };
+
+    void accept(Visitor* visitor);
+
+    inline const std::map<std::string, Node>& getProductRoots() const {
+        return mProductRoots;
+    }
+
+private:
+    DISALLOW_COPY_AND_ASSIGN(DominatorTree);
+
+    std::map<std::string, Node> mProductRoots;
+};
+
+} // namespace aapt
+
+#endif // AAPT_DOMINATOR_TREE_H
diff --git a/tools/aapt2/DominatorTree_test.cpp b/tools/aapt2/DominatorTree_test.cpp
new file mode 100644
index 0000000..fb850e4da
--- /dev/null
+++ b/tools/aapt2/DominatorTree_test.cpp
@@ -0,0 +1,151 @@
+/*
+ * 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.
+ */
+
+#include "DominatorTree.h"
+#include "test/Test.h"
+#include "util/Util.h"
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+namespace {
+
+class PrettyPrinter : public DominatorTree::Visitor {
+public:
+    explicit PrettyPrinter(const int indent = 2) : mIndent(indent) {
+    }
+
+    void visitTree(const std::string& product, DominatorTree::Node* root) override {
+        for (auto& child : root->children()) {
+            visitNode(child.get(), 0);
+        }
+    }
+
+    std::string toString(DominatorTree* tree) {
+        mBuffer.str("");
+        mBuffer.clear();
+        tree->accept(this);
+        return mBuffer.str();
+    }
+
+private:
+    void visitConfig(const DominatorTree::Node* node, const int indent) {
+        auto configString = node->value()->config.toString();
+        mBuffer << std::string(indent, ' ')
+                << (configString.isEmpty() ? "<default>" : configString)
+                << std::endl;
+    }
+
+    void visitNode(const DominatorTree::Node* node, const int indent) {
+        visitConfig(node, indent);
+        for (const auto& child : node->children()) {
+            visitNode(child.get(), indent + mIndent);
+        }
+    }
+
+    std::stringstream mBuffer;
+    const int mIndent = 2;
+};
+
+} // namespace
+
+TEST(DominatorTreeTest, DefaultDominatesEverything) {
+    const ConfigDescription defaultConfig = {};
+    const ConfigDescription landConfig = test::parseConfigOrDie("land");
+    const ConfigDescription sw600dpLandConfig = test::parseConfigOrDie("sw600dp-land-v13");
+
+    std::vector<std::unique_ptr<ResourceConfigValue>> configs;
+    configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
+    configs.push_back(util::make_unique<ResourceConfigValue>(landConfig, ""));
+    configs.push_back(util::make_unique<ResourceConfigValue>(sw600dpLandConfig, ""));
+
+    DominatorTree tree(configs);
+    PrettyPrinter printer;
+
+    std::string expected =
+            "<default>\n"
+            "  land\n"
+            "  sw600dp-land-v13\n";
+    EXPECT_EQ(expected, printer.toString(&tree));
+}
+
+TEST(DominatorTreeTest, ProductsAreDominatedSeparately) {
+    const ConfigDescription defaultConfig = {};
+    const ConfigDescription landConfig = test::parseConfigOrDie("land");
+    const ConfigDescription sw600dpLandConfig = test::parseConfigOrDie("sw600dp-land-v13");
+
+    std::vector<std::unique_ptr<ResourceConfigValue>> configs;
+    configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
+    configs.push_back(util::make_unique<ResourceConfigValue>(landConfig, ""));
+    configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, "phablet"));
+    configs.push_back(util::make_unique<ResourceConfigValue>(sw600dpLandConfig, "phablet"));
+
+    DominatorTree tree(configs);
+    PrettyPrinter printer;
+
+    std::string expected =
+            "<default>\n"
+            "  land\n"
+            "<default>\n"
+            "  sw600dp-land-v13\n";
+    EXPECT_EQ(expected, printer.toString(&tree));
+}
+
+TEST(DominatorTreeTest, MoreSpecificConfigurationsAreDominated) {
+    const ConfigDescription defaultConfig = {};
+    const ConfigDescription enConfig = test::parseConfigOrDie("en");
+    const ConfigDescription enV21Config = test::parseConfigOrDie("en-v21");
+    const ConfigDescription ldrtlConfig = test::parseConfigOrDie("ldrtl-v4");
+    const ConfigDescription ldrtlXhdpiConfig = test::parseConfigOrDie("ldrtl-xhdpi-v4");
+    const ConfigDescription sw300dpConfig = test::parseConfigOrDie("sw300dp-v13");
+    const ConfigDescription sw540dpConfig = test::parseConfigOrDie("sw540dp-v14");
+    const ConfigDescription sw600dpConfig = test::parseConfigOrDie("sw600dp-v14");
+    const ConfigDescription sw720dpConfig = test::parseConfigOrDie("sw720dp-v13");
+    const ConfigDescription v20Config = test::parseConfigOrDie("v20");
+
+    std::vector<std::unique_ptr<ResourceConfigValue>> configs;
+    configs.push_back(util::make_unique<ResourceConfigValue>(defaultConfig, ""));
+    configs.push_back(util::make_unique<ResourceConfigValue>(enConfig, ""));
+    configs.push_back(util::make_unique<ResourceConfigValue>(enV21Config, ""));
+    configs.push_back(util::make_unique<ResourceConfigValue>(ldrtlConfig, ""));
+    configs.push_back(util::make_unique<ResourceConfigValue>(ldrtlXhdpiConfig, ""));
+    configs.push_back(util::make_unique<ResourceConfigValue>(sw300dpConfig, ""));
+    configs.push_back(util::make_unique<ResourceConfigValue>(sw540dpConfig, ""));
+    configs.push_back(util::make_unique<ResourceConfigValue>(sw600dpConfig, ""));
+    configs.push_back(util::make_unique<ResourceConfigValue>(sw720dpConfig, ""));
+    configs.push_back(util::make_unique<ResourceConfigValue>(v20Config, ""));
+
+    DominatorTree tree(configs);
+    PrettyPrinter printer;
+
+    std::string expected =
+            "<default>\n"
+            "  en\n"
+            "    en-v21\n"
+            "  ldrtl-v4\n"
+            "    ldrtl-xhdpi-v4\n"
+            "  sw300dp-v13\n"
+            "    sw540dp-v14\n"
+            "      sw600dp-v14\n"
+            "    sw720dp-v13\n"
+            "  v20\n";
+    EXPECT_EQ(expected, printer.toString(&tree));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/Compile.cpp b/tools/aapt2/compile/Compile.cpp
index e0f37ec..dbd8062 100644
--- a/tools/aapt2/compile/Compile.cpp
+++ b/tools/aapt2/compile/Compile.cpp
@@ -36,6 +36,8 @@
 #include <google/protobuf/io/zero_copy_stream_impl_lite.h>
 #include <google/protobuf/io/coded_stream.h>
 
+#include <android-base/errors.h>
+#include <android-base/file.h>
 #include <dirent.h>
 #include <fstream>
 #include <string>
@@ -359,6 +361,9 @@
 static bool compileXml(IAaptContext* context, const CompileOptions& options,
                        const ResourcePathData& pathData, IArchiveWriter* writer,
                        const std::string& outputPath) {
+    if (context->verbose()) {
+        context->getDiagnostics()->note(DiagMessage(pathData.source) << "compiling XML");
+    }
 
     std::unique_ptr<xml::XmlResource> xmlRes;
     {
@@ -431,9 +436,43 @@
     return true;
 }
 
+class BigBufferOutputStream : public io::OutputStream {
+public:
+    explicit BigBufferOutputStream(BigBuffer* buffer) : mBuffer(buffer) {
+    }
+
+    bool Next(void** data, int* len) override {
+        size_t count;
+        *data = mBuffer->nextBlock(&count);
+        *len = static_cast<int>(count);
+        return true;
+    }
+
+    void BackUp(int count) override {
+        mBuffer->backUp(count);
+    }
+
+    int64_t ByteCount() const override {
+        return mBuffer->size();
+    }
+
+    bool HadError() const override {
+        return false;
+    }
+
+private:
+    BigBuffer* mBuffer;
+
+    DISALLOW_COPY_AND_ASSIGN(BigBufferOutputStream);
+};
+
 static bool compilePng(IAaptContext* context, const CompileOptions& options,
                        const ResourcePathData& pathData, IArchiveWriter* writer,
                        const std::string& outputPath) {
+    if (context->verbose()) {
+        context->getDiagnostics()->note(DiagMessage(pathData.source) << "compiling PNG");
+    }
+
     BigBuffer buffer(4096);
     ResourceFile resFile;
     resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
@@ -441,16 +480,90 @@
     resFile.source = pathData.source;
 
     {
-        std::ifstream fin(pathData.source.path, std::ifstream::binary);
-        if (!fin) {
-            context->getDiagnostics()->error(DiagMessage(pathData.source) << strerror(errno));
+        std::string content;
+        if (!android::base::ReadFileToString(pathData.source.path, &content)) {
+            context->getDiagnostics()->error(DiagMessage(pathData.source)
+                                             << android::base::SystemErrorCodeToString(errno));
             return false;
         }
 
-        Png png(context->getDiagnostics());
-        if (!png.process(pathData.source, &fin, &buffer, {})) {
+        BigBuffer crunchedPngBuffer(4096);
+        BigBufferOutputStream crunchedPngBufferOut(&crunchedPngBuffer);
+
+        // Ensure that we only keep the chunks we care about if we end up
+        // using the original PNG instead of the crunched one.
+        PngChunkFilter pngChunkFilter(content);
+        std::unique_ptr<Image> image = readPng(context, &pngChunkFilter);
+        if (!image) {
             return false;
         }
+
+        std::unique_ptr<NinePatch> ninePatch;
+        if (pathData.extension == "9.png") {
+            std::string err;
+            ninePatch = NinePatch::create(image->rows.get(), image->width, image->height, &err);
+            if (!ninePatch) {
+                context->getDiagnostics()->error(DiagMessage() << err);
+                return false;
+            }
+
+            // Remove the 1px border around the NinePatch.
+            // Basically the row array is shifted up by 1, and the length is treated
+            // as height - 2.
+            // For each row, shift the array to the left by 1, and treat the length as width - 2.
+            image->width -= 2;
+            image->height -= 2;
+            memmove(image->rows.get(), image->rows.get() + 1, image->height * sizeof(uint8_t**));
+            for (int32_t h = 0; h < image->height; h++) {
+                memmove(image->rows[h], image->rows[h] + 4, image->width * 4);
+            }
+
+            if (context->verbose()) {
+                context->getDiagnostics()->note(DiagMessage(pathData.source)
+                                                << "9-patch: " << *ninePatch);
+            }
+        }
+
+        // Write the crunched PNG.
+        if (!writePng(context, image.get(), ninePatch.get(), &crunchedPngBufferOut, {})) {
+            return false;
+        }
+
+        if (ninePatch != nullptr
+                || crunchedPngBufferOut.ByteCount() <= pngChunkFilter.ByteCount()) {
+            // No matter what, we must use the re-encoded PNG, even if it is larger.
+            // 9-patch images must be re-encoded since their borders are stripped.
+            buffer.appendBuffer(std::move(crunchedPngBuffer));
+        } else {
+            // The re-encoded PNG is larger than the original, and there is
+            // no mandatory transformation. Use the original.
+            if (context->verbose()) {
+                context->getDiagnostics()->note(DiagMessage(pathData.source)
+                                                << "original PNG is smaller than crunched PNG"
+                                                << ", using original");
+            }
+
+            PngChunkFilter pngChunkFilterAgain(content);
+            BigBuffer filteredPngBuffer(4096);
+            BigBufferOutputStream filteredPngBufferOut(&filteredPngBuffer);
+            io::copy(&filteredPngBufferOut, &pngChunkFilterAgain);
+            buffer.appendBuffer(std::move(filteredPngBuffer));
+        }
+
+        if (context->verbose()) {
+            // For debugging only, use the legacy PNG cruncher and compare the resulting file sizes.
+            // This will help catch exotic cases where the new code may generate larger PNGs.
+            std::stringstream legacyStream(content);
+            BigBuffer legacyBuffer(4096);
+            Png png(context->getDiagnostics());
+            if (!png.process(pathData.source, &legacyStream, &legacyBuffer, {})) {
+                return false;
+            }
+
+            context->getDiagnostics()->note(DiagMessage(pathData.source)
+                                            << "legacy=" << legacyBuffer.size()
+                                            << " new=" << buffer.size());
+        }
     }
 
     if (!writeHeaderAndBufferToWriter(outputPath, resFile, buffer, writer,
@@ -463,6 +576,10 @@
 static bool compileFile(IAaptContext* context, const CompileOptions& options,
                         const ResourcePathData& pathData, IArchiveWriter* writer,
                         const std::string& outputPath) {
+    if (context->verbose()) {
+        context->getDiagnostics()->note(DiagMessage(pathData.source) << "compiling file");
+    }
+
     BigBuffer buffer(256);
     ResourceFile resFile;
     resFile.name = ResourceName({}, *parseResourceType(pathData.resourceDir), pathData.name);
diff --git a/tools/aapt2/compile/Image.h b/tools/aapt2/compile/Image.h
new file mode 100644
index 0000000..fda6a3a
--- /dev/null
+++ b/tools/aapt2/compile/Image.h
@@ -0,0 +1,201 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_COMPILE_IMAGE_H
+#define AAPT_COMPILE_IMAGE_H
+
+#include <android-base/macros.h>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+/**
+ * An in-memory image, loaded from disk, with pixels in RGBA_8888 format.
+ */
+class Image {
+public:
+    explicit Image() = default;
+
+    /**
+     * A `height` sized array of pointers, where each element points to a
+     * `width` sized row of RGBA_8888 pixels.
+     */
+    std::unique_ptr<uint8_t*[]> rows;
+
+    /**
+     * The width of the image in RGBA_8888 pixels. This is int32_t because of 9-patch data
+     * format limitations.
+     */
+    int32_t width = 0;
+
+    /**
+     * The height of the image in RGBA_8888 pixels. This is int32_t because of 9-patch data
+     * format limitations.
+     */
+    int32_t height = 0;
+
+    /**
+     * Buffer to the raw image data stored sequentially.
+     * Use `rows` to access the data on a row-by-row basis.
+     */
+    std::unique_ptr<uint8_t[]> data;
+
+private:
+    DISALLOW_COPY_AND_ASSIGN(Image);
+};
+
+/**
+ * A range of pixel values, starting at 'start' and ending before 'end' exclusive. Or rather [a, b).
+ */
+struct Range {
+    int32_t start = 0;
+    int32_t end = 0;
+
+    explicit Range() = default;
+    inline explicit Range(int32_t s, int32_t e) : start(s), end(e) {
+    }
+};
+
+inline bool operator==(const Range& left, const Range& right) {
+    return left.start == right.start && left.end == right.end;
+}
+
+/**
+ * Inset lengths from all edges of a rectangle. `left` and `top` are measured from the left and top
+ * edges, while `right` and `bottom` are measured from the right and bottom edges, respectively.
+ */
+struct Bounds {
+    int32_t left = 0;
+    int32_t top = 0;
+    int32_t right = 0;
+    int32_t bottom = 0;
+
+    explicit Bounds() = default;
+    inline explicit Bounds(int32_t l, int32_t t, int32_t r, int32_t b) :
+            left(l), top(t), right(r), bottom(b) {
+    }
+
+    bool nonZero() const;
+};
+
+inline bool Bounds::nonZero() const {
+    return left != 0 || top != 0 || right != 0 || bottom != 0;
+}
+
+inline bool operator==(const Bounds& left, const Bounds& right) {
+    return left.left == right.left && left.top == right.top &&
+            left.right == right.right && left.bottom == right.bottom;
+}
+
+/**
+ * Contains 9-patch data from a source image. All measurements exclude the 1px border of the
+ * source 9-patch image.
+ */
+class NinePatch {
+public:
+    static std::unique_ptr<NinePatch> create(uint8_t** rows,
+                                             const int32_t width, const int32_t height,
+                                             std::string* errOut);
+
+    /**
+     * Packs the RGBA_8888 data pointed to by pixel into a uint32_t
+     * with format 0xAARRGGBB (the way 9-patch expects it).
+     */
+    static uint32_t packRGBA(const uint8_t* pixel);
+
+    /**
+     * 9-patch content padding/insets. All positions are relative to the 9-patch
+     * NOT including the 1px thick source border.
+     */
+    Bounds padding;
+
+    /**
+     * Optical layout bounds/insets. This overrides the padding for
+     * layout purposes. All positions are relative to the 9-patch
+     * NOT including the 1px thick source border.
+     * See https://developer.android.com/about/versions/android-4.3.html#OpticalBounds
+     */
+    Bounds layoutBounds;
+
+    /**
+     * Outline of the image, calculated based on opacity.
+     */
+    Bounds outline;
+
+    /**
+     * The computed radius of the outline. If non-zero, the outline is a rounded-rect.
+     */
+    float outlineRadius = 0.0f;
+
+    /**
+     * The largest alpha value within the outline.
+     */
+    uint32_t outlineAlpha = 0x000000ffu;
+
+    /**
+     * Horizontal regions of the image that are stretchable.
+     * All positions are relative to the 9-patch
+     * NOT including the 1px thick source border.
+     */
+    std::vector<Range> horizontalStretchRegions;
+
+    /**
+     * Vertical regions of the image that are stretchable.
+     * All positions are relative to the 9-patch
+     * NOT including the 1px thick source border.
+     */
+    std::vector<Range> verticalStretchRegions;
+
+    /**
+     * The colors within each region, fixed or stretchable.
+     * For w*h regions, the color of region (x,y) is addressable
+     * via index y*w + x.
+     */
+    std::vector<uint32_t> regionColors;
+
+    /**
+     * Returns serialized data containing the original basic 9-patch meta data.
+     * Optical layout bounds and round rect outline data must be serialized
+     * separately using serializeOpticalLayoutBounds() and serializeRoundedRectOutline().
+     */
+    std::unique_ptr<uint8_t[]> serializeBase(size_t* outLen) const;
+
+    /**
+     * Serializes the layout bounds.
+     */
+    std::unique_ptr<uint8_t[]> serializeLayoutBounds(size_t* outLen) const;
+
+    /**
+     * Serializes the rounded-rect outline.
+     */
+    std::unique_ptr<uint8_t[]> serializeRoundedRectOutline(size_t* outLen) const;
+
+private:
+    explicit NinePatch() = default;
+
+    DISALLOW_COPY_AND_ASSIGN(NinePatch);
+};
+
+::std::ostream& operator<<(::std::ostream& out, const Range& range);
+::std::ostream& operator<<(::std::ostream& out, const Bounds& bounds);
+::std::ostream& operator<<(::std::ostream& out, const NinePatch& ninePatch);
+
+} // namespace aapt
+
+#endif /* AAPT_COMPILE_IMAGE_H */
diff --git a/tools/aapt2/compile/NinePatch.cpp b/tools/aapt2/compile/NinePatch.cpp
new file mode 100644
index 0000000..0fc1c5d
--- /dev/null
+++ b/tools/aapt2/compile/NinePatch.cpp
@@ -0,0 +1,676 @@
+/*
+ * 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.
+ */
+
+#include "compile/Image.h"
+#include "util/StringPiece.h"
+#include "util/Util.h"
+
+#include <androidfw/ResourceTypes.h>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace aapt {
+
+// Colors in the format 0xAARRGGBB (the way 9-patch expects it).
+constexpr static const uint32_t kColorOpaqueWhite = 0xffffffffu;
+constexpr static const uint32_t kColorOpaqueBlack = 0xff000000u;
+constexpr static const uint32_t kColorOpaqueRed   = 0xffff0000u;
+
+constexpr static const uint32_t kPrimaryColor = kColorOpaqueBlack;
+constexpr static const uint32_t kSecondaryColor = kColorOpaqueRed;
+
+/**
+ * Returns the alpha value encoded in the 0xAARRGBB encoded pixel.
+ */
+static uint32_t getAlpha(uint32_t color);
+
+/**
+ * Determines whether a color on an ImageLine is valid.
+ * A 9patch image may use a transparent color as neutral,
+ * or a fully opaque white color as neutral, based on the
+ * pixel color at (0,0) of the image. One or the other is fine,
+ * but we need to ensure consistency throughout the image.
+ */
+class ColorValidator {
+public:
+    virtual ~ColorValidator() = default;
+
+    /**
+     * Returns true if the color specified is a neutral color
+     * (no padding, stretching, or optical bounds).
+     */
+    virtual bool isNeutralColor(uint32_t color) const = 0;
+
+    /**
+     * Returns true if the color is either a neutral color
+     * or one denoting padding, stretching, or optical bounds.
+     */
+    bool isValidColor(uint32_t color) const {
+        switch (color) {
+        case kPrimaryColor:
+        case kSecondaryColor:
+            return true;
+        }
+        return isNeutralColor(color);
+    }
+};
+
+// Walks an ImageLine and records Ranges of primary and secondary colors.
+// The primary color is black and is used to denote a padding or stretching range,
+// depending on which border we're iterating over.
+// The secondary color is red and is used to denote optical bounds.
+//
+// An ImageLine is a templated-interface that would look something like this if it
+// were polymorphic:
+//
+// class ImageLine {
+// public:
+//      virtual int32_t getLength() const = 0;
+//      virtual uint32_t getColor(int32_t idx) const = 0;
+// };
+//
+template <typename ImageLine>
+static bool fillRanges(const ImageLine* imageLine,
+                       const ColorValidator* colorValidator,
+                       std::vector<Range>* primaryRanges,
+                       std::vector<Range>* secondaryRanges,
+                       std::string* err) {
+    const int32_t length = imageLine->getLength();
+
+    uint32_t lastColor = 0xffffffffu;
+    for (int32_t idx = 1; idx < length - 1; idx++) {
+        const uint32_t color = imageLine->getColor(idx);
+        if (!colorValidator->isValidColor(color)) {
+            *err = "found an invalid color";
+            return false;
+        }
+
+        if (color != lastColor) {
+            // We are ending a range. Which range?
+            // note: encode the x offset without the final 1 pixel border.
+            if (lastColor == kPrimaryColor) {
+                primaryRanges->back().end = idx - 1;
+            } else if (lastColor == kSecondaryColor) {
+                secondaryRanges->back().end = idx - 1;
+            }
+
+            // We are starting a range. Which range?
+            // note: encode the x offset without the final 1 pixel border.
+            if (color == kPrimaryColor) {
+                primaryRanges->push_back(Range(idx - 1, length - 2));
+            } else if (color == kSecondaryColor) {
+                secondaryRanges->push_back(Range(idx - 1, length - 2));
+            }
+            lastColor = color;
+        }
+    }
+    return true;
+}
+
+/**
+ * Iterates over a row in an image. Implements the templated ImageLine interface.
+ */
+class HorizontalImageLine {
+public:
+    explicit HorizontalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
+                                 int32_t length) :
+            mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mLength(length) {
+    }
+
+    inline int32_t getLength() const {
+        return mLength;
+    }
+
+    inline uint32_t getColor(int32_t idx) const {
+        return NinePatch::packRGBA(mRows[mYOffset] + (idx + mXOffset) * 4);
+    }
+
+private:
+    uint8_t** mRows;
+    int32_t mXOffset, mYOffset, mLength;
+
+    DISALLOW_COPY_AND_ASSIGN(HorizontalImageLine);
+};
+
+/**
+ * Iterates over a column in an image. Implements the templated ImageLine interface.
+ */
+class VerticalImageLine {
+public:
+    explicit VerticalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
+                               int32_t length) :
+            mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mLength(length) {
+    }
+
+    inline int32_t getLength() const {
+        return mLength;
+    }
+
+    inline uint32_t getColor(int32_t idx) const {
+        return NinePatch::packRGBA(mRows[mYOffset + idx] + (mXOffset * 4));
+    }
+
+private:
+    uint8_t** mRows;
+    int32_t mXOffset, mYOffset, mLength;
+
+    DISALLOW_COPY_AND_ASSIGN(VerticalImageLine);
+};
+
+class DiagonalImageLine {
+public:
+    explicit DiagonalImageLine(uint8_t** rows, int32_t xOffset, int32_t yOffset,
+                               int32_t xStep, int32_t yStep, int32_t length) :
+            mRows(rows), mXOffset(xOffset), mYOffset(yOffset), mXStep(xStep), mYStep(yStep),
+            mLength(length) {
+    }
+
+    inline int32_t getLength() const {
+        return mLength;
+    }
+
+    inline uint32_t getColor(int32_t idx) const {
+        return NinePatch::packRGBA(
+                mRows[mYOffset + (idx * mYStep)] + ((idx + mXOffset) * mXStep) * 4);
+    }
+
+private:
+    uint8_t** mRows;
+    int32_t mXOffset, mYOffset, mXStep, mYStep, mLength;
+
+    DISALLOW_COPY_AND_ASSIGN(DiagonalImageLine);
+};
+
+class TransparentNeutralColorValidator : public ColorValidator {
+public:
+    bool isNeutralColor(uint32_t color) const override {
+        return getAlpha(color) == 0;
+    }
+};
+
+class WhiteNeutralColorValidator : public ColorValidator {
+public:
+    bool isNeutralColor(uint32_t color) const override {
+        return color == kColorOpaqueWhite;
+    }
+};
+
+inline static uint32_t getAlpha(uint32_t color) {
+    return (color & 0xff000000u) >> 24;
+}
+
+static bool populateBounds(const std::vector<Range>& padding,
+                           const std::vector<Range>& layoutBounds,
+                           const std::vector<Range>& stretchRegions,
+                           const int32_t length,
+                           int32_t* paddingStart, int32_t* paddingEnd,
+                           int32_t* layoutStart, int32_t* layoutEnd,
+                           const StringPiece& edgeName,
+                           std::string* err) {
+    if (padding.size() > 1) {
+        std::stringstream errStream;
+        errStream << "too many padding sections on " << edgeName << " border";
+        *err = errStream.str();
+        return false;
+    }
+
+    *paddingStart = 0;
+    *paddingEnd = 0;
+    if (!padding.empty()) {
+        const Range& range = padding.front();
+        *paddingStart = range.start;
+        *paddingEnd = length - range.end;
+    } else if (!stretchRegions.empty()) {
+        // No padding was defined. Compute the padding from the first and last
+        // stretch regions.
+        *paddingStart = stretchRegions.front().start;
+        *paddingEnd = length - stretchRegions.back().end;
+    }
+
+    if (layoutBounds.size() > 2) {
+        std::stringstream errStream;
+        errStream << "too many layout bounds sections on " << edgeName << " border";
+        *err = errStream.str();
+        return false;
+    }
+
+    *layoutStart = 0;
+    *layoutEnd = 0;
+    if (layoutBounds.size() >= 1) {
+        const Range& range = layoutBounds.front();
+        // If there is only one layout bound segment, it might not start at 0, but then it should
+        // end at length.
+        if (range.start != 0 && range.end != length) {
+            std::stringstream errStream;
+            errStream << "layout bounds on " << edgeName << " border must start at edge";
+            *err = errStream.str();
+            return false;
+        }
+        *layoutStart = range.end;
+
+        if (layoutBounds.size() >= 2) {
+            const Range& range = layoutBounds.back();
+            if (range.end != length) {
+                std::stringstream errStream;
+                errStream << "layout bounds on " << edgeName << " border must start at edge";
+                *err = errStream.str();
+                return false;
+            }
+            *layoutEnd = length - range.start;
+        }
+    }
+    return true;
+}
+
+static int32_t calculateSegmentCount(const std::vector<Range>& stretchRegions, int32_t length) {
+    if (stretchRegions.size() == 0) {
+        return 0;
+    }
+
+    const bool startIsFixed = stretchRegions.front().start != 0;
+    const bool endIsFixed = stretchRegions.back().end != length;
+    int32_t modifier = 0;
+    if (startIsFixed && endIsFixed) {
+        modifier = 1;
+    } else if (!startIsFixed && !endIsFixed) {
+        modifier = -1;
+    }
+    return static_cast<int32_t>(stretchRegions.size()) * 2 + modifier;
+}
+
+static uint32_t getRegionColor(uint8_t** rows, const Bounds& region) {
+    // Sample the first pixel to compare against.
+    const uint32_t expectedColor = NinePatch::packRGBA(rows[region.top] + region.left * 4);
+    for (int32_t y = region.top; y < region.bottom; y++) {
+        const uint8_t* row = rows[y];
+        for (int32_t x = region.left; x < region.right; x++) {
+            const uint32_t color = NinePatch::packRGBA(row + x * 4);
+            if (getAlpha(color) == 0) {
+                // The color is transparent.
+                // If the expectedColor is not transparent, NO_COLOR.
+                if (getAlpha(expectedColor) != 0) {
+                    return android::Res_png_9patch::NO_COLOR;
+                }
+            } else if (color != expectedColor) {
+                return android::Res_png_9patch::NO_COLOR;
+            }
+        }
+    }
+
+    if (getAlpha(expectedColor) == 0) {
+        return android::Res_png_9patch::TRANSPARENT_COLOR;
+    }
+    return expectedColor;
+}
+
+// Fills outColors with each 9-patch section's colour. If the whole section is transparent,
+// it gets the special TRANSPARENT colour. If the whole section is the same colour, it is assigned
+// that colour. Otherwise it gets the special NO_COLOR colour.
+//
+// Note that the rows contain the 9-patch 1px border, and the indices in the stretch regions are
+// already offset to exclude the border. This means that each time the rows are accessed,
+// the indices must be offset by 1.
+//
+// width and height also include the 9-patch 1px border.
+static void calculateRegionColors(uint8_t** rows,
+                                  const std::vector<Range>& horizontalStretchRegions,
+                                  const std::vector<Range>& verticalStretchRegions,
+                                  const int32_t width, const int32_t height,
+                                  std::vector<uint32_t>* outColors) {
+    int32_t nextTop = 0;
+    Bounds bounds;
+    auto rowIter = verticalStretchRegions.begin();
+    while (nextTop != height) {
+        if (rowIter != verticalStretchRegions.end()) {
+            if (nextTop != rowIter->start) {
+                // This is a fixed segment.
+                // Offset the bounds by 1 to accommodate the border.
+                bounds.top = nextTop + 1;
+                bounds.bottom = rowIter->start + 1;
+                nextTop = rowIter->start;
+            } else {
+                // This is a stretchy segment.
+                // Offset the bounds by 1 to accommodate the border.
+                bounds.top = rowIter->start + 1;
+                bounds.bottom = rowIter->end + 1;
+                nextTop = rowIter->end;
+                ++rowIter;
+            }
+        } else {
+            // This is the end, fixed section.
+            // Offset the bounds by 1 to accommodate the border.
+            bounds.top = nextTop + 1;
+            bounds.bottom = height + 1;
+            nextTop = height;
+         }
+
+        int32_t nextLeft = 0;
+        auto colIter = horizontalStretchRegions.begin();
+        while (nextLeft != width) {
+            if (colIter != horizontalStretchRegions.end()) {
+                if (nextLeft != colIter->start) {
+                    // This is a fixed segment.
+                    // Offset the bounds by 1 to accommodate the border.
+                    bounds.left = nextLeft + 1;
+                    bounds.right = colIter->start + 1;
+                    nextLeft = colIter->start;
+                } else {
+                    // This is a stretchy segment.
+                    // Offset the bounds by 1 to accommodate the border.
+                    bounds.left = colIter->start + 1;
+                    bounds.right = colIter->end + 1;
+                    nextLeft = colIter->end;
+                    ++colIter;
+                }
+            } else {
+                // This is the end, fixed section.
+                // Offset the bounds by 1 to accommodate the border.
+                bounds.left = nextLeft + 1;
+                bounds.right = width + 1;
+                nextLeft = width;
+            }
+            outColors->push_back(getRegionColor(rows, bounds));
+        }
+    }
+}
+
+// Calculates the insets of a row/column of pixels based on where the largest alpha value begins
+// (on both sides).
+template <typename ImageLine>
+static void findOutlineInsets(const ImageLine* imageLine, int32_t* outStart, int32_t* outEnd) {
+    *outStart = 0;
+    *outEnd = 0;
+
+    const int32_t length = imageLine->getLength();
+    if (length < 3) {
+        return;
+    }
+
+    // If the length is odd, we want both sides to process the center pixel,
+    // so we use two different midpoints (to account for < and <= in the different loops).
+    const int32_t mid2 = length / 2;
+    const int32_t mid1 = mid2 + (length % 2);
+
+    uint32_t maxAlpha = 0;
+    for (int32_t i = 0; i < mid1 && maxAlpha != 0xff; i++) {
+        uint32_t alpha = getAlpha(imageLine->getColor(i));
+        if (alpha > maxAlpha) {
+            maxAlpha = alpha;
+            *outStart = i;
+        }
+    }
+
+    maxAlpha = 0;
+    for (int32_t i = length - 1; i >= mid2 && maxAlpha != 0xff; i--) {
+        uint32_t alpha = getAlpha(imageLine->getColor(i));
+        if (alpha > maxAlpha) {
+            maxAlpha = alpha;
+            *outEnd = length - (i + 1);
+        }
+    }
+    return;
+}
+
+template <typename ImageLine>
+static uint32_t findMaxAlpha(const ImageLine* imageLine) {
+    const int32_t length = imageLine->getLength();
+    uint32_t maxAlpha = 0;
+    for (int32_t idx = 0; idx < length && maxAlpha != 0xff; idx++) {
+        uint32_t alpha = getAlpha(imageLine->getColor(idx));
+        if (alpha > maxAlpha) {
+            maxAlpha = alpha;
+        }
+    }
+    return maxAlpha;
+}
+
+// Pack the pixels in as 0xAARRGGBB (as 9-patch expects it).
+uint32_t NinePatch::packRGBA(const uint8_t* pixel) {
+    return (pixel[3] << 24) | (pixel[0] << 16) | (pixel[1] << 8) | pixel[2];
+}
+
+std::unique_ptr<NinePatch> NinePatch::create(uint8_t** rows,
+                                             const int32_t width, const int32_t height,
+                                             std::string* err) {
+    if (width < 3 || height < 3) {
+        *err = "image must be at least 3x3 (1x1 image with 1 pixel border)";
+        return {};
+    }
+
+    std::vector<Range> horizontalPadding;
+    std::vector<Range> horizontalOpticalBounds;
+    std::vector<Range> verticalPadding;
+    std::vector<Range> verticalOpticalBounds;
+    std::vector<Range> unexpectedRanges;
+    std::unique_ptr<ColorValidator> colorValidator;
+
+    if (rows[0][3] == 0) {
+        colorValidator = util::make_unique<TransparentNeutralColorValidator>();
+    } else if (packRGBA(rows[0]) == kColorOpaqueWhite) {
+        colorValidator = util::make_unique<WhiteNeutralColorValidator>();
+    } else {
+        *err = "top-left corner pixel must be either opaque white or transparent";
+        return {};
+    }
+
+    // Private constructor, can't use make_unique.
+    auto ninePatch = std::unique_ptr<NinePatch>(new NinePatch());
+
+    HorizontalImageLine topRow(rows, 0, 0, width);
+    if (!fillRanges(&topRow, colorValidator.get(), &ninePatch->horizontalStretchRegions,
+                    &unexpectedRanges, err)) {
+        return {};
+    }
+
+    if (!unexpectedRanges.empty()) {
+        const Range& range = unexpectedRanges[0];
+        std::stringstream errStream;
+        errStream << "found unexpected optical bounds (red pixel) on top border "
+                << "at x=" << range.start + 1;
+        *err = errStream.str();
+        return {};
+    }
+
+    VerticalImageLine leftCol(rows, 0, 0, height);
+    if (!fillRanges(&leftCol, colorValidator.get(), &ninePatch->verticalStretchRegions,
+                    &unexpectedRanges, err)) {
+        return {};
+    }
+
+    if (!unexpectedRanges.empty()) {
+        const Range& range = unexpectedRanges[0];
+        std::stringstream errStream;
+        errStream << "found unexpected optical bounds (red pixel) on left border "
+                << "at y=" << range.start + 1;
+        return {};
+    }
+
+    HorizontalImageLine bottomRow(rows, 0, height - 1, width);
+    if (!fillRanges(&bottomRow, colorValidator.get(), &horizontalPadding,
+                    &horizontalOpticalBounds, err)) {
+        return {};
+    }
+
+    if (!populateBounds(horizontalPadding, horizontalOpticalBounds,
+                        ninePatch->horizontalStretchRegions, width - 2,
+                        &ninePatch->padding.left, &ninePatch->padding.right,
+                        &ninePatch->layoutBounds.left, &ninePatch->layoutBounds.right,
+                        "bottom", err)) {
+        return {};
+    }
+
+    VerticalImageLine rightCol(rows, width - 1, 0, height);
+    if (!fillRanges(&rightCol, colorValidator.get(), &verticalPadding,
+                    &verticalOpticalBounds, err)) {
+        return {};
+    }
+
+    if (!populateBounds(verticalPadding, verticalOpticalBounds,
+                        ninePatch->verticalStretchRegions, height - 2,
+                        &ninePatch->padding.top, &ninePatch->padding.bottom,
+                        &ninePatch->layoutBounds.top, &ninePatch->layoutBounds.bottom,
+                        "right", err)) {
+        return {};
+    }
+
+    // Fill the region colors of the 9-patch.
+    const int32_t numRows = calculateSegmentCount(ninePatch->horizontalStretchRegions, width - 2);
+    const int32_t numCols = calculateSegmentCount(ninePatch->verticalStretchRegions, height - 2);
+    if ((int64_t) numRows * (int64_t) numCols > 0x7f) {
+        *err = "too many regions in 9-patch";
+        return {};
+    }
+
+    ninePatch->regionColors.reserve(numRows * numCols);
+    calculateRegionColors(rows, ninePatch->horizontalStretchRegions,
+                          ninePatch->verticalStretchRegions,
+                          width - 2, height - 2,
+                          &ninePatch->regionColors);
+
+    // Compute the outline based on opacity.
+
+    // Find left and right extent of 9-patch content on center row.
+    HorizontalImageLine midRow(rows, 1, height / 2, width - 2);
+    findOutlineInsets(&midRow, &ninePatch->outline.left, &ninePatch->outline.right);
+
+    // Find top and bottom extent of 9-patch content on center column.
+    VerticalImageLine midCol(rows, width / 2, 1, height - 2);
+    findOutlineInsets(&midCol, &ninePatch->outline.top, &ninePatch->outline.bottom);
+
+    const int32_t outlineWidth = (width - 2) - ninePatch->outline.left - ninePatch->outline.right;
+    const int32_t outlineHeight = (height - 2) - ninePatch->outline.top - ninePatch->outline.bottom;
+
+    // Find the largest alpha value within the outline area.
+    HorizontalImageLine outlineMidRow(rows,
+                                      1 + ninePatch->outline.left,
+                                      1 + ninePatch->outline.top + (outlineHeight / 2),
+                                      outlineWidth);
+    VerticalImageLine outlineMidCol(rows,
+                                    1 + ninePatch->outline.left + (outlineWidth / 2),
+                                    1 + ninePatch->outline.top,
+                                    outlineHeight);
+    ninePatch->outlineAlpha = std::max(findMaxAlpha(&outlineMidRow), findMaxAlpha(&outlineMidCol));
+
+    // Assuming the image is a round rect, compute the radius by marching
+    // diagonally from the top left corner towards the center.
+    DiagonalImageLine diagonal(rows, 1 + ninePatch->outline.left, 1 + ninePatch->outline.top,
+                               1, 1, std::min(outlineWidth, outlineHeight));
+    int32_t topLeft, bottomRight;
+    findOutlineInsets(&diagonal, &topLeft, &bottomRight);
+
+    /* Determine source radius based upon inset:
+     *     sqrt(r^2 + r^2) = sqrt(i^2 + i^2) + r
+     *     sqrt(2) * r = sqrt(2) * i + r
+     *     (sqrt(2) - 1) * r = sqrt(2) * i
+     *     r = sqrt(2) / (sqrt(2) - 1) * i
+     */
+    ninePatch->outlineRadius = 3.4142f * topLeft;
+    return ninePatch;
+}
+
+std::unique_ptr<uint8_t[]> NinePatch::serializeBase(size_t* outLen) const {
+    android::Res_png_9patch data;
+    data.numXDivs = static_cast<uint8_t>(horizontalStretchRegions.size()) * 2;
+    data.numYDivs = static_cast<uint8_t>(verticalStretchRegions.size()) * 2;
+    data.numColors = static_cast<uint8_t>(regionColors.size());
+    data.paddingLeft = padding.left;
+    data.paddingRight = padding.right;
+    data.paddingTop = padding.top;
+    data.paddingBottom = padding.bottom;
+
+    auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[data.serializedSize()]);
+    android::Res_png_9patch::serialize(data,
+                                       (const int32_t*) horizontalStretchRegions.data(),
+                                       (const int32_t*) verticalStretchRegions.data(),
+                                       regionColors.data(),
+                                       buffer.get());
+    // Convert to file endianness.
+    reinterpret_cast<android::Res_png_9patch*>(buffer.get())->deviceToFile();
+
+    *outLen = data.serializedSize();
+    return buffer;
+}
+
+std::unique_ptr<uint8_t[]> NinePatch::serializeLayoutBounds(size_t* outLen) const {
+    size_t chunkLen = sizeof(uint32_t) * 4;
+    auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunkLen]);
+    uint8_t* cursor = buffer.get();
+
+    memcpy(cursor, &layoutBounds.left, sizeof(layoutBounds.left));
+    cursor += sizeof(layoutBounds.left);
+
+    memcpy(cursor, &layoutBounds.top, sizeof(layoutBounds.top));
+    cursor += sizeof(layoutBounds.top);
+
+    memcpy(cursor, &layoutBounds.right, sizeof(layoutBounds.right));
+    cursor += sizeof(layoutBounds.right);
+
+    memcpy(cursor, &layoutBounds.bottom, sizeof(layoutBounds.bottom));
+    cursor += sizeof(layoutBounds.bottom);
+
+    *outLen = chunkLen;
+    return buffer;
+}
+
+std::unique_ptr<uint8_t[]> NinePatch::serializeRoundedRectOutline(size_t* outLen) const {
+    size_t chunkLen = sizeof(uint32_t) * 6;
+    auto buffer = std::unique_ptr<uint8_t[]>(new uint8_t[chunkLen]);
+    uint8_t* cursor = buffer.get();
+
+    memcpy(cursor, &outline.left, sizeof(outline.left));
+    cursor += sizeof(outline.left);
+
+    memcpy(cursor, &outline.top, sizeof(outline.top));
+    cursor += sizeof(outline.top);
+
+    memcpy(cursor, &outline.right, sizeof(outline.right));
+    cursor += sizeof(outline.right);
+
+    memcpy(cursor, &outline.bottom, sizeof(outline.bottom));
+    cursor += sizeof(outline.bottom);
+
+    *((float*) cursor) = outlineRadius;
+    cursor += sizeof(outlineRadius);
+
+    *((uint32_t*) cursor) = outlineAlpha;
+
+    *outLen = chunkLen;
+    return buffer;
+}
+
+::std::ostream& operator<<(::std::ostream& out, const Range& range) {
+    return out << "[" << range.start << ", " << range.end << ")";
+}
+
+::std::ostream& operator<<(::std::ostream& out, const Bounds& bounds) {
+    return out << "l=" << bounds.left
+            << " t=" << bounds.top
+            << " r=" << bounds.right
+            << " b=" << bounds.bottom;
+}
+
+::std::ostream& operator<<(::std::ostream& out, const NinePatch& ninePatch) {
+    return out << "horizontalStretch:" << util::joiner(ninePatch.horizontalStretchRegions, " ")
+            << " verticalStretch:" << util::joiner(ninePatch.verticalStretchRegions, " ")
+            << " padding: " << ninePatch.padding
+            << ", bounds: " << ninePatch.layoutBounds
+            << ", outline: " << ninePatch.outline
+            << " rad=" << ninePatch.outlineRadius
+            << " alpha=" << ninePatch.outlineAlpha;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/NinePatch_test.cpp b/tools/aapt2/compile/NinePatch_test.cpp
new file mode 100644
index 0000000..3106ff8
--- /dev/null
+++ b/tools/aapt2/compile/NinePatch_test.cpp
@@ -0,0 +1,358 @@
+/*
+ * 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.
+ */
+
+#include "compile/Image.h"
+#include "test/Test.h"
+
+namespace aapt {
+
+// Pixels are in RGBA_8888 packing.
+
+#define RED   "\xff\x00\x00\xff"
+#define BLUE  "\x00\x00\xff\xff"
+#define GREEN "\xff\x00\x00\xff"
+#define GR_70 "\xff\x00\x00\xb3"
+#define GR_50 "\xff\x00\x00\x80"
+#define GR_20 "\xff\x00\x00\x33"
+#define BLACK "\x00\x00\x00\xff"
+#define WHITE "\xff\xff\xff\xff"
+#define TRANS "\x00\x00\x00\x00"
+
+static uint8_t* k2x2[] = {
+        (uint8_t*) WHITE WHITE,
+        (uint8_t*) WHITE WHITE,
+};
+
+static uint8_t* kMixedNeutralColor3x3[] = {
+        (uint8_t*) WHITE BLACK TRANS,
+        (uint8_t*) TRANS RED   TRANS,
+        (uint8_t*) WHITE WHITE WHITE,
+};
+
+static uint8_t* kTransparentNeutralColor3x3[] = {
+        (uint8_t*) TRANS BLACK TRANS,
+        (uint8_t*) BLACK RED   BLACK,
+        (uint8_t*) TRANS BLACK TRANS,
+};
+
+static uint8_t* kSingleStretch7x6[] = {
+        (uint8_t*) WHITE WHITE BLACK BLACK BLACK WHITE WHITE,
+        (uint8_t*) WHITE RED   RED   RED   RED   RED   WHITE,
+        (uint8_t*) BLACK RED   RED   RED   RED   RED   WHITE,
+        (uint8_t*) BLACK RED   RED   RED   RED   RED   WHITE,
+        (uint8_t*) WHITE RED   RED   RED   RED   RED   WHITE,
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kMultipleStretch10x7[] = {
+        (uint8_t*) WHITE WHITE BLACK WHITE BLACK BLACK WHITE BLACK WHITE WHITE,
+        (uint8_t*) BLACK RED   BLUE  RED   BLUE  BLUE  RED   BLUE  RED   WHITE,
+        (uint8_t*) BLACK RED   BLUE  RED   BLUE  BLUE  RED   BLUE  RED   WHITE,
+        (uint8_t*) WHITE RED   BLUE  RED   BLUE  BLUE  RED   BLUE  RED   WHITE,
+        (uint8_t*) BLACK RED   BLUE  RED   BLUE  BLUE  RED   BLUE  RED   WHITE,
+        (uint8_t*) BLACK RED   BLUE  RED   BLUE  BLUE  RED   BLUE  RED   WHITE,
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kPadding6x5[] = {
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE,
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE,
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE BLACK,
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE,
+        (uint8_t*) WHITE WHITE BLACK BLACK WHITE WHITE,
+};
+
+static uint8_t* kLayoutBoundsWrongEdge3x3[] = {
+        (uint8_t*) WHITE RED   WHITE,
+        (uint8_t*) RED   WHITE WHITE,
+        (uint8_t*) WHITE WHITE WHITE,
+};
+
+static uint8_t* kLayoutBoundsNotEdgeAligned5x5[] = {
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+        (uint8_t*) WHITE WHITE WHITE WHITE RED,
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+        (uint8_t*) WHITE WHITE RED   WHITE WHITE,
+};
+
+static uint8_t* kLayoutBounds5x5[] = {
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+        (uint8_t*) WHITE WHITE WHITE WHITE RED,
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+        (uint8_t*) WHITE WHITE WHITE WHITE RED,
+        (uint8_t*) WHITE RED   WHITE RED   WHITE,
+};
+
+static uint8_t* kAsymmetricLayoutBounds5x5[] = {
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+        (uint8_t*) WHITE WHITE WHITE WHITE RED,
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+        (uint8_t*) WHITE RED   WHITE WHITE WHITE,
+};
+
+static uint8_t* kPaddingAndLayoutBounds5x5[] = {
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+        (uint8_t*) WHITE WHITE WHITE WHITE RED,
+        (uint8_t*) WHITE WHITE WHITE WHITE BLACK,
+        (uint8_t*) WHITE WHITE WHITE WHITE RED,
+        (uint8_t*) WHITE RED   BLACK RED   WHITE,
+};
+
+static uint8_t* kColorfulImage5x5[] = {
+        (uint8_t*) WHITE BLACK WHITE BLACK WHITE,
+        (uint8_t*) BLACK RED   BLUE  GREEN WHITE,
+        (uint8_t*) BLACK RED   GREEN GREEN WHITE,
+        (uint8_t*) WHITE TRANS BLUE  GREEN WHITE,
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kOutlineOpaque10x10[] = {
+        (uint8_t*) WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
+        (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS GREEN GREEN GREEN GREEN TRANS TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kOutlineTranslucent10x10[] = {
+        (uint8_t*) WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
+        (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+        (uint8_t*) WHITE TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+        (uint8_t*) WHITE TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kOutlineOffsetTranslucent12x10[] = {
+        (uint8_t*) WHITE WHITE WHITE BLACK BLACK BLACK BLACK BLACK BLACK BLACK BLACK WHITE,
+        (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS TRANS GR_20 GR_50 GR_70 GR_70 GR_50 GR_20 TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_50 GR_50 GR_50 GR_50 TRANS TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS TRANS TRANS GR_20 GR_20 GR_20 GR_20 TRANS TRANS WHITE,
+        (uint8_t*) WHITE TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS TRANS WHITE,
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kOutlineRadius5x5[] = {
+        (uint8_t*) WHITE BLACK BLACK BLACK WHITE,
+        (uint8_t*) BLACK TRANS GREEN TRANS WHITE,
+        (uint8_t*) BLACK GREEN GREEN GREEN WHITE,
+        (uint8_t*) BLACK TRANS GREEN TRANS WHITE,
+        (uint8_t*) WHITE WHITE WHITE WHITE WHITE,
+};
+
+static uint8_t* kStretchAndPadding5x5[] = {
+        (uint8_t*) WHITE WHITE BLACK WHITE WHITE,
+        (uint8_t*) WHITE RED   RED   RED   WHITE,
+        (uint8_t*) BLACK RED   RED   RED   BLACK,
+        (uint8_t*) WHITE RED   RED   RED   WHITE,
+        (uint8_t*) WHITE WHITE BLACK WHITE WHITE,
+};
+
+TEST(NinePatchTest, Minimum3x3) {
+    std::string err;
+    EXPECT_EQ(nullptr, NinePatch::create(k2x2, 2, 2, &err));
+    EXPECT_FALSE(err.empty());
+}
+
+TEST(NinePatchTest, MixedNeutralColors) {
+    std::string err;
+    EXPECT_EQ(nullptr, NinePatch::create(kMixedNeutralColor3x3, 3, 3, &err));
+    EXPECT_FALSE(err.empty());
+}
+
+TEST(NinePatchTest, TransparentNeutralColor) {
+    std::string err;
+    EXPECT_NE(nullptr, NinePatch::create(kTransparentNeutralColor3x3, 3, 3, &err));
+}
+
+TEST(NinePatchTest, SingleStretchRegion) {
+    std::string err;
+    std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kSingleStretch7x6, 7, 6, &err);
+    ASSERT_NE(nullptr, ninePatch);
+
+    ASSERT_EQ(1u, ninePatch->horizontalStretchRegions.size());
+    ASSERT_EQ(1u, ninePatch->verticalStretchRegions.size());
+
+    EXPECT_EQ(Range(1, 4), ninePatch->horizontalStretchRegions.front());
+    EXPECT_EQ(Range(1, 3), ninePatch->verticalStretchRegions.front());
+}
+
+TEST(NinePatchTest, MultipleStretchRegions) {
+    std::string err;
+    std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kMultipleStretch10x7, 10, 7, &err);
+    ASSERT_NE(nullptr, ninePatch);
+
+    ASSERT_EQ(3u, ninePatch->horizontalStretchRegions.size());
+    ASSERT_EQ(2u, ninePatch->verticalStretchRegions.size());
+
+    EXPECT_EQ(Range(1, 2), ninePatch->horizontalStretchRegions[0]);
+    EXPECT_EQ(Range(3, 5), ninePatch->horizontalStretchRegions[1]);
+    EXPECT_EQ(Range(6, 7), ninePatch->horizontalStretchRegions[2]);
+
+    EXPECT_EQ(Range(0, 2), ninePatch->verticalStretchRegions[0]);
+    EXPECT_EQ(Range(3, 5), ninePatch->verticalStretchRegions[1]);
+}
+
+TEST(NinePatchTest, InferPaddingFromStretchRegions) {
+    std::string err;
+    std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kMultipleStretch10x7, 10, 7, &err);
+    ASSERT_NE(nullptr, ninePatch);
+    EXPECT_EQ(Bounds(1, 0, 1, 0), ninePatch->padding);
+}
+
+TEST(NinePatchTest, Padding) {
+    std::string err;
+    std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kPadding6x5, 6, 5, &err);
+    ASSERT_NE(nullptr, ninePatch);
+    EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->padding);
+}
+
+TEST(NinePatchTest, LayoutBoundsAreOnWrongEdge) {
+    std::string err;
+    EXPECT_EQ(nullptr, NinePatch::create(kLayoutBoundsWrongEdge3x3, 3, 3, &err));
+    EXPECT_FALSE(err.empty());
+}
+
+TEST(NinePatchTest, LayoutBoundsMustTouchEdges) {
+    std::string err;
+    EXPECT_EQ(nullptr, NinePatch::create(kLayoutBoundsNotEdgeAligned5x5, 5, 5, &err));
+    EXPECT_FALSE(err.empty());
+}
+
+TEST(NinePatchTest, LayoutBounds) {
+    std::string err;
+    std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kLayoutBounds5x5, 5, 5, &err);
+    ASSERT_NE(nullptr, ninePatch);
+    EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->layoutBounds);
+
+    ninePatch = NinePatch::create(kAsymmetricLayoutBounds5x5, 5, 5, &err);
+    ASSERT_NE(nullptr, ninePatch);
+    EXPECT_EQ(Bounds(1, 1, 0, 0), ninePatch->layoutBounds);
+}
+
+TEST(NinePatchTest, PaddingAndLayoutBounds) {
+    std::string err;
+    std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kPaddingAndLayoutBounds5x5, 5, 5,
+                                                             &err);
+    ASSERT_NE(nullptr, ninePatch);
+    EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->padding);
+    EXPECT_EQ(Bounds(1, 1, 1, 1), ninePatch->layoutBounds);
+}
+
+TEST(NinePatchTest, RegionColorsAreCorrect) {
+    std::string err;
+    std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kColorfulImage5x5, 5, 5, &err);
+    ASSERT_NE(nullptr, ninePatch);
+
+    std::vector<uint32_t> expectedColors = {
+            NinePatch::packRGBA((uint8_t*) RED),
+            (uint32_t) android::Res_png_9patch::NO_COLOR,
+            NinePatch::packRGBA((uint8_t*) GREEN),
+            (uint32_t) android::Res_png_9patch::TRANSPARENT_COLOR,
+            NinePatch::packRGBA((uint8_t*) BLUE),
+            NinePatch::packRGBA((uint8_t*) GREEN),
+    };
+    EXPECT_EQ(expectedColors, ninePatch->regionColors);
+}
+
+TEST(NinePatchTest, OutlineFromOpaqueImage) {
+    std::string err;
+    std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineOpaque10x10, 10, 10, &err);
+    ASSERT_NE(nullptr, ninePatch);
+    EXPECT_EQ(Bounds(2, 2, 2, 2), ninePatch->outline);
+    EXPECT_EQ(0x000000ffu, ninePatch->outlineAlpha);
+    EXPECT_EQ(0.0f, ninePatch->outlineRadius);
+}
+
+TEST(NinePatchTest, OutlineFromTranslucentImage) {
+    std::string err;
+    std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineTranslucent10x10, 10, 10,
+                                                             &err);
+    ASSERT_NE(nullptr, ninePatch);
+    EXPECT_EQ(Bounds(3, 3, 3, 3), ninePatch->outline);
+    EXPECT_EQ(0x000000b3u, ninePatch->outlineAlpha);
+    EXPECT_EQ(0.0f, ninePatch->outlineRadius);
+}
+
+TEST(NinePatchTest, OutlineFromOffCenterImage) {
+    std::string err;
+    std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineOffsetTranslucent12x10, 12, 10,
+                                                             &err);
+    ASSERT_NE(nullptr, ninePatch);
+
+    // TODO(adamlesinski): The old AAPT algorithm searches from the outside to the middle
+    // for each inset. If the outline is shifted, the search may not find a closer bounds.
+    // This check should be:
+    //   EXPECT_EQ(Bounds(5, 3, 3, 3), ninePatch->outline);
+    // but until I know what behaviour I'm breaking, I will leave it at the incorrect:
+    EXPECT_EQ(Bounds(4, 3, 3, 3), ninePatch->outline);
+
+    EXPECT_EQ(0x000000b3u, ninePatch->outlineAlpha);
+    EXPECT_EQ(0.0f, ninePatch->outlineRadius);
+}
+
+TEST(NinePatchTest, OutlineRadius) {
+    std::string err;
+    std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kOutlineRadius5x5, 5, 5, &err);
+    ASSERT_NE(nullptr, ninePatch);
+    EXPECT_EQ(Bounds(0, 0, 0, 0), ninePatch->outline);
+    EXPECT_EQ(3.4142f, ninePatch->outlineRadius);
+}
+
+::testing::AssertionResult bigEndianOne(uint8_t* cursor) {
+    if (cursor[0] == 0 && cursor[1] == 0 && cursor[2] == 0 && cursor[3] == 1) {
+        return ::testing::AssertionSuccess();
+    }
+    return ::testing::AssertionFailure() << "Not BigEndian 1";
+}
+
+TEST(NinePatchTest, SerializePngEndianness) {
+    std::string err;
+    std::unique_ptr<NinePatch> ninePatch = NinePatch::create(kStretchAndPadding5x5, 5, 5, &err);
+    ASSERT_NE(nullptr, ninePatch);
+
+    size_t len;
+    std::unique_ptr<uint8_t[]> data = ninePatch->serializeBase(&len);
+    ASSERT_NE(nullptr, data);
+    ASSERT_NE(0u, len);
+
+    // Skip past wasDeserialized + numXDivs + numYDivs + numColors + xDivsOffset + yDivsOffset
+    // (12 bytes)
+    uint8_t* cursor = data.get() + 12;
+
+    // Check that padding is big-endian. Expecting value 1.
+    EXPECT_TRUE(bigEndianOne(cursor));
+    EXPECT_TRUE(bigEndianOne(cursor + 4));
+    EXPECT_TRUE(bigEndianOne(cursor + 8));
+    EXPECT_TRUE(bigEndianOne(cursor + 12));
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/Png.h b/tools/aapt2/compile/Png.h
index f835b06e..4a15d95 100644
--- a/tools/aapt2/compile/Png.h
+++ b/tools/aapt2/compile/Png.h
@@ -17,10 +17,14 @@
 #ifndef AAPT_PNG_H
 #define AAPT_PNG_H
 
-#include "util/BigBuffer.h"
 #include "Diagnostics.h"
 #include "Source.h"
+#include "compile/Image.h"
+#include "io/Io.h"
+#include "process/IResourceTableConsumer.h"
+#include "util/BigBuffer.h"
 
+#include <android-base/macros.h>
 #include <iostream>
 #include <string>
 
@@ -40,8 +44,51 @@
 
 private:
     IDiagnostics* mDiag;
+
+    DISALLOW_COPY_AND_ASSIGN(Png);
 };
 
+/**
+ * An InputStream that filters out unimportant PNG chunks.
+ */
+class PngChunkFilter : public io::InputStream {
+public:
+    explicit PngChunkFilter(const StringPiece& data);
+
+    bool Next(const void** buffer, int* len) override;
+    void BackUp(int count) override;
+    bool Skip(int count) override;
+
+    int64_t ByteCount() const override {
+        return static_cast<int64_t>(mWindowStart);
+    }
+
+    bool HadError() const override {
+        return mError;
+    }
+
+private:
+    bool consumeWindow(const void** buffer, int* len);
+
+    StringPiece mData;
+    size_t mWindowStart = 0;
+    size_t mWindowEnd = 0;
+    bool mError = false;
+
+    DISALLOW_COPY_AND_ASSIGN(PngChunkFilter);
+};
+
+/**
+ * Reads a PNG from the InputStream into memory as an RGBA Image.
+ */
+std::unique_ptr<Image> readPng(IAaptContext* context, io::InputStream* in);
+
+/**
+ * Writes the RGBA Image, with optional 9-patch meta-data, into the OutputStream as a PNG.
+ */
+bool writePng(IAaptContext* context, const Image* image, const NinePatch* ninePatch,
+              io::OutputStream* out, const PngOptions& options);
+
 } // namespace aapt
 
 #endif // AAPT_PNG_H
diff --git a/tools/aapt2/compile/PngChunkFilter.cpp b/tools/aapt2/compile/PngChunkFilter.cpp
new file mode 100644
index 0000000..70a881f
--- /dev/null
+++ b/tools/aapt2/compile/PngChunkFilter.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+#include "compile/Png.h"
+#include "io/Io.h"
+#include "util/StringPiece.h"
+
+namespace aapt {
+
+static constexpr const char* kPngSignature = "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a";
+
+// Useful helper function that encodes individual bytes into a uint32
+// at compile time.
+constexpr uint32_t u32(uint8_t a, uint8_t b, uint8_t c, uint8_t d) {
+    return (((uint32_t) a) << 24)
+            | (((uint32_t) b) << 16)
+            | (((uint32_t) c) << 8)
+            | ((uint32_t) d);
+}
+
+// Whitelist of PNG chunk types that we want to keep in the resulting PNG.
+enum PngChunkTypes {
+    kPngChunkIHDR = u32(73, 72, 68, 82),
+    kPngChunkIDAT = u32(73, 68, 65, 84),
+    kPngChunkIEND = u32(73, 69, 78, 68),
+    kPngChunkPLTE = u32(80, 76, 84, 69),
+    kPngChunktRNS = u32(116, 82, 78, 83),
+    kPngChunksRGB = u32(115, 82, 71, 66),
+};
+
+static uint32_t peek32LE(const char* data) {
+    uint32_t word = ((uint32_t) data[0]) & 0x000000ff;
+    word <<= 8;
+    word |= ((uint32_t) data[1]) & 0x000000ff;
+    word <<= 8;
+    word |= ((uint32_t) data[2]) & 0x000000ff;
+    word <<= 8;
+    word |= ((uint32_t) data[3]) & 0x000000ff;
+    return word;
+}
+
+static bool isPngChunkWhitelisted(uint32_t type) {
+    switch (type) {
+    case kPngChunkIHDR:
+    case kPngChunkIDAT:
+    case kPngChunkIEND:
+    case kPngChunkPLTE:
+    case kPngChunktRNS:
+    case kPngChunksRGB:
+        return true;
+    default:
+        return false;
+    }
+}
+
+PngChunkFilter::PngChunkFilter(const StringPiece& data) : mData(data) {
+    if (util::stringStartsWith(mData, kPngSignature)) {
+        mWindowStart = 0;
+        mWindowEnd = strlen(kPngSignature);
+    } else {
+        mError = true;
+    }
+}
+
+bool PngChunkFilter::consumeWindow(const void** buffer, int* len) {
+    if (mWindowStart != mWindowEnd) {
+        // We have bytes to give from our window.
+        const int bytesRead = (int) (mWindowEnd - mWindowStart);
+        *buffer = mData.data() + mWindowStart;
+        *len = bytesRead;
+        mWindowStart = mWindowEnd;
+        return true;
+    }
+    return false;
+}
+
+bool PngChunkFilter::Next(const void** buffer, int* len) {
+    if (mError) {
+        return false;
+    }
+
+    // In case BackUp was called, we must consume the window.
+    if (consumeWindow(buffer, len)) {
+        return true;
+    }
+
+    // Advance the window as far as possible (until we meet a chunk that
+    // we want to strip).
+    while (mWindowEnd < mData.size()) {
+        // Chunk length (4 bytes) + type (4 bytes) + crc32 (4 bytes) = 12 bytes.
+        const size_t kMinChunkHeaderSize = 3 * sizeof(uint32_t);
+
+        // Is there enough room for a chunk header?
+        if (mData.size() - mWindowStart < kMinChunkHeaderSize) {
+            mError = true;
+            return false;
+        }
+
+        // Verify the chunk length.
+        const uint32_t chunkLen = peek32LE(mData.data() + mWindowEnd);
+        if (((uint64_t) chunkLen) + ((uint64_t) mWindowEnd) + sizeof(uint32_t) > mData.size()) {
+            // Overflow.
+            mError = true;
+            return false;
+        }
+
+        // Do we strip this chunk?
+        const uint32_t chunkType = peek32LE(mData.data() + mWindowEnd + sizeof(uint32_t));
+        if (isPngChunkWhitelisted(chunkType)) {
+            // Advance the window to include this chunk.
+            mWindowEnd += kMinChunkHeaderSize + chunkLen;
+        } else {
+            // We want to strip this chunk. If we accumulated a window,
+            // we must return the window now.
+            if (mWindowStart != mWindowEnd) {
+                break;
+            }
+
+            // The window is empty, so we can advance past this chunk
+            // and keep looking for the next good chunk,
+            mWindowEnd += kMinChunkHeaderSize + chunkLen;
+            mWindowStart = mWindowEnd;
+        }
+    }
+
+    if (consumeWindow(buffer, len)) {
+        return true;
+    }
+    return false;
+}
+
+void PngChunkFilter::BackUp(int count) {
+    if (mError) {
+        return;
+    }
+    mWindowStart -= count;
+}
+
+bool PngChunkFilter::Skip(int count) {
+    if (mError) {
+        return false;
+    }
+
+    const void* buffer;
+    int len;
+    while (count > 0) {
+        if (!Next(&buffer, &len)) {
+            return false;
+        }
+        if (len > count) {
+            BackUp(len - count);
+            count = 0;
+        } else {
+            count -= len;
+        }
+    }
+    return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/compile/PngCrunch.cpp b/tools/aapt2/compile/PngCrunch.cpp
new file mode 100644
index 0000000..a2e3f4f
--- /dev/null
+++ b/tools/aapt2/compile/PngCrunch.cpp
@@ -0,0 +1,724 @@
+/*
+ * 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.
+ */
+
+#include "compile/Png.h"
+
+#include <algorithm>
+#include <android-base/errors.h>
+#include <android-base/macros.h>
+#include <png.h>
+#include <unordered_map>
+#include <unordered_set>
+#include <zlib.h>
+
+namespace aapt {
+
+// Size in bytes of the PNG signature.
+constexpr size_t kPngSignatureSize = 8u;
+
+/**
+ * Custom deleter that destroys libpng read and info structs.
+ */
+class PngReadStructDeleter {
+public:
+    explicit PngReadStructDeleter(png_structp readPtr, png_infop infoPtr) :
+            mReadPtr(readPtr), mInfoPtr(infoPtr) {
+    }
+
+    ~PngReadStructDeleter() {
+        png_destroy_read_struct(&mReadPtr, &mInfoPtr, nullptr);
+    }
+
+private:
+    png_structp mReadPtr;
+    png_infop mInfoPtr;
+
+    DISALLOW_COPY_AND_ASSIGN(PngReadStructDeleter);
+};
+
+/**
+ * Custom deleter that destroys libpng write and info structs.
+ */
+class PngWriteStructDeleter {
+public:
+    explicit PngWriteStructDeleter(png_structp writePtr, png_infop infoPtr) :
+            mWritePtr(writePtr), mInfoPtr(infoPtr) {
+    }
+
+    ~PngWriteStructDeleter() {
+        png_destroy_write_struct(&mWritePtr, &mInfoPtr);
+    }
+
+private:
+    png_structp mWritePtr;
+    png_infop mInfoPtr;
+
+    DISALLOW_COPY_AND_ASSIGN(PngWriteStructDeleter);
+};
+
+// Custom warning logging method that uses IDiagnostics.
+static void logWarning(png_structp pngPtr, png_const_charp warningMsg) {
+    IDiagnostics* diag = (IDiagnostics*) png_get_error_ptr(pngPtr);
+    diag->warn(DiagMessage() << warningMsg);
+}
+
+// Custom error logging method that uses IDiagnostics.
+static void logError(png_structp pngPtr, png_const_charp errorMsg) {
+    IDiagnostics* diag = (IDiagnostics*) png_get_error_ptr(pngPtr);
+    diag->error(DiagMessage() << errorMsg);
+}
+
+static void readDataFromStream(png_structp pngPtr, png_bytep buffer, png_size_t len) {
+    io::InputStream* in = (io::InputStream*) png_get_io_ptr(pngPtr);
+
+    const void* inBuffer;
+    int inLen;
+    if (!in->Next(&inBuffer, &inLen)) {
+        if (in->HadError()) {
+            std::string err = in->GetError();
+            png_error(pngPtr, err.c_str());
+        }
+        return;
+    }
+
+    const size_t bytesRead = std::min(static_cast<size_t>(inLen), len);
+    memcpy(buffer, inBuffer, bytesRead);
+    if (bytesRead != static_cast<size_t>(inLen)) {
+        in->BackUp(inLen - static_cast<int>(bytesRead));
+    }
+}
+
+static void writeDataToStream(png_structp pngPtr, png_bytep buffer, png_size_t len) {
+    io::OutputStream* out = (io::OutputStream*) png_get_io_ptr(pngPtr);
+
+    void* outBuffer;
+    int outLen;
+    while (len > 0) {
+        if (!out->Next(&outBuffer, &outLen)) {
+            if (out->HadError()) {
+                std::string err = out->GetError();
+                png_error(pngPtr, err.c_str());
+            }
+            return;
+        }
+
+        const size_t bytesWritten = std::min(static_cast<size_t>(outLen), len);
+        memcpy(outBuffer, buffer, bytesWritten);
+
+        // Advance the input buffer.
+        buffer += bytesWritten;
+        len -= bytesWritten;
+
+        // Advance the output buffer.
+        outLen -= static_cast<int>(bytesWritten);
+    }
+
+    // If the entire output buffer wasn't used, backup.
+    if (outLen > 0) {
+        out->BackUp(outLen);
+    }
+}
+
+std::unique_ptr<Image> readPng(IAaptContext* context, io::InputStream* in) {
+    // Read the first 8 bytes of the file looking for the PNG signature.
+    // Bail early if it does not match.
+    const png_byte* signature;
+    int bufferSize;
+    if (!in->Next((const void**) &signature, &bufferSize)) {
+        context->getDiagnostics()->error(DiagMessage()
+                                         << android::base::SystemErrorCodeToString(errno));
+        return {};
+    }
+
+    if (static_cast<size_t>(bufferSize) < kPngSignatureSize
+            || png_sig_cmp(signature, 0, kPngSignatureSize) != 0) {
+        context->getDiagnostics()->error(DiagMessage()
+                                         << "file signature does not match PNG signature");
+        return {};
+    }
+
+    // Start at the beginning of the first chunk.
+    in->BackUp(bufferSize - static_cast<int>(kPngSignatureSize));
+
+    // Create and initialize the png_struct with the default error and warning handlers.
+    // The header version is also passed in to ensure that this was built against the same
+    // version of libpng.
+    png_structp readPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+    if (readPtr == nullptr) {
+        context->getDiagnostics()->error(DiagMessage()
+                                         << "failed to create libpng read png_struct");
+        return {};
+    }
+
+    // Create and initialize the memory for image header and data.
+    png_infop infoPtr = png_create_info_struct(readPtr);
+    if (infoPtr == nullptr) {
+        context->getDiagnostics()->error(DiagMessage() << "failed to create libpng read png_info");
+        png_destroy_read_struct(&readPtr, nullptr, nullptr);
+        return {};
+    }
+
+    // Automatically release PNG resources at end of scope.
+    PngReadStructDeleter pngReadDeleter(readPtr, infoPtr);
+
+    // libpng uses longjmp to jump to an error handling routine.
+    // setjmp will only return true if it was jumped to, aka there was
+    // an error.
+    if (setjmp(png_jmpbuf(readPtr))) {
+        return {};
+    }
+
+    // Handle warnings ourselves via IDiagnostics.
+    png_set_error_fn(readPtr, (png_voidp) context->getDiagnostics(), logError, logWarning);
+
+    // Set up the read functions which read from our custom data sources.
+    png_set_read_fn(readPtr, (png_voidp) in, readDataFromStream);
+
+    // Skip the signature that we already read.
+    png_set_sig_bytes(readPtr, kPngSignatureSize);
+
+    // Read the chunk headers.
+    png_read_info(readPtr, infoPtr);
+
+    // Extract image meta-data from the various chunk headers.
+    uint32_t width, height;
+    int bitDepth, colorType, interlaceMethod, compressionMethod, filterMethod;
+    png_get_IHDR(readPtr, infoPtr, &width, &height, &bitDepth, &colorType, &interlaceMethod,
+                 &compressionMethod, &filterMethod);
+
+    // When the image is read, expand it so that it is in RGBA 8888 format
+    // so that image handling is uniform.
+
+    if (colorType == PNG_COLOR_TYPE_PALETTE) {
+        png_set_palette_to_rgb(readPtr);
+    }
+
+    if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) {
+        png_set_expand_gray_1_2_4_to_8(readPtr);
+    }
+
+    if (png_get_valid(readPtr, infoPtr, PNG_INFO_tRNS)) {
+        png_set_tRNS_to_alpha(readPtr);
+    }
+
+    if (bitDepth == 16) {
+        png_set_strip_16(readPtr);
+    }
+
+    if (!(colorType & PNG_COLOR_MASK_ALPHA)) {
+        png_set_add_alpha(readPtr, 0xFF, PNG_FILLER_AFTER);
+    }
+
+    if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+        png_set_gray_to_rgb(readPtr);
+    }
+
+    if (interlaceMethod != PNG_INTERLACE_NONE) {
+        png_set_interlace_handling(readPtr);
+    }
+
+    // Once all the options for reading have been set, we need to flush
+    // them to libpng.
+    png_read_update_info(readPtr, infoPtr);
+
+    // 9-patch uses int32_t to index images, so we cap the image dimensions to something
+    // that can always be represented by 9-patch.
+    if (width > std::numeric_limits<int32_t>::max() ||
+            height > std::numeric_limits<int32_t>::max()) {
+        context->getDiagnostics()->error(DiagMessage() << "PNG image dimensions are too large: "
+                                         << width << "x" << height);
+        return {};
+    }
+
+    std::unique_ptr<Image> outputImage = util::make_unique<Image>();
+    outputImage->width = static_cast<int32_t>(width);
+    outputImage->height = static_cast<int32_t>(height);
+
+    const size_t rowBytes = png_get_rowbytes(readPtr, infoPtr);
+    assert(rowBytes == 4 * width); // RGBA
+
+    // Allocate one large block to hold the image.
+    outputImage->data = std::unique_ptr<uint8_t[]>(new uint8_t[height * rowBytes]);
+
+    // Create an array of rows that index into the data block.
+    outputImage->rows = std::unique_ptr<uint8_t*[]>(new uint8_t*[height]);
+    for (uint32_t h = 0; h < height; h++) {
+        outputImage->rows[h] = outputImage->data.get() + (h * rowBytes);
+    }
+
+    // Actually read the image pixels.
+    png_read_image(readPtr, outputImage->rows.get());
+
+    // Finish reading. This will read any other chunks after the image data.
+    png_read_end(readPtr, infoPtr);
+
+    return outputImage;
+}
+
+/**
+ * Experimentally chosen constant to be added to the overhead of using color type
+ * PNG_COLOR_TYPE_PALETTE to account for the uncompressability of the palette chunk.
+ * Without this, many small PNGs encoded with palettes are larger after compression than
+ * the same PNGs encoded as RGBA.
+ */
+constexpr static const size_t kPaletteOverheadConstant = 1024u * 10u;
+
+// Pick a color type by which to encode the image, based on which color type will take
+// the least amount of disk space.
+//
+// 9-patch images traditionally have not been encoded with palettes.
+// The original rationale was to avoid dithering until after scaling,
+// but I don't think this would be an issue with palettes. Either way,
+// our naive size estimation tends to be wrong for small images like 9-patches
+// and using palettes balloons the size of the resulting 9-patch.
+// In order to not regress in size, restrict 9-patch to not use palettes.
+
+// The options are:
+//
+// - RGB
+// - RGBA
+// - RGB + cheap alpha
+// - Color palette
+// - Color palette + cheap alpha
+// - Color palette + alpha palette
+// - Grayscale
+// - Grayscale + cheap alpha
+// - Grayscale + alpha
+//
+static int pickColorType(int32_t width, int32_t height,
+                         bool grayScale, bool convertibleToGrayScale, bool hasNinePatch,
+                         size_t colorPaletteSize, size_t alphaPaletteSize) {
+    const size_t paletteChunkSize = 16 + colorPaletteSize * 3;
+    const size_t alphaChunkSize = 16 + alphaPaletteSize;
+    const size_t colorAlphaDataChunkSize = 16 + 4 * width * height;
+    const size_t colorDataChunkSize = 16 + 3 * width * height;
+    const size_t grayScaleAlphaDataChunkSize = 16 + 2 * width * height;
+    const size_t paletteDataChunkSize = 16 + width * height;
+
+    if (grayScale) {
+        if (alphaPaletteSize == 0) {
+            // This is the smallest the data can be.
+            return PNG_COLOR_TYPE_GRAY;
+        } else if (colorPaletteSize <= 256 && !hasNinePatch) {
+            // This grayscale has alpha and can fit within a palette.
+            // See if it is worth fitting into a palette.
+            const size_t paletteThreshold = paletteChunkSize + alphaChunkSize +
+                    paletteDataChunkSize + kPaletteOverheadConstant;
+            if (grayScaleAlphaDataChunkSize > paletteThreshold) {
+                return PNG_COLOR_TYPE_PALETTE;
+            }
+        }
+        return PNG_COLOR_TYPE_GRAY_ALPHA;
+    }
+
+
+    if (colorPaletteSize <= 256 && !hasNinePatch) {
+        // This image can fit inside a palette. Let's see if it is worth it.
+        size_t totalSizeWithPalette = paletteDataChunkSize + paletteChunkSize;
+        size_t totalSizeWithoutPalette = colorDataChunkSize;
+        if (alphaPaletteSize > 0) {
+            totalSizeWithPalette += alphaPaletteSize;
+            totalSizeWithoutPalette = colorAlphaDataChunkSize;
+        }
+
+        if (totalSizeWithoutPalette > totalSizeWithPalette + kPaletteOverheadConstant) {
+            return PNG_COLOR_TYPE_PALETTE;
+        }
+    }
+
+    if (convertibleToGrayScale) {
+        if (alphaPaletteSize == 0) {
+            return PNG_COLOR_TYPE_GRAY;
+        } else {
+            return PNG_COLOR_TYPE_GRAY_ALPHA;
+        }
+    }
+
+    if (alphaPaletteSize == 0) {
+        return PNG_COLOR_TYPE_RGB;
+    }
+    return PNG_COLOR_TYPE_RGBA;
+}
+
+// Assigns indices to the color and alpha palettes, encodes them, and then invokes
+// png_set_PLTE/png_set_tRNS.
+// This must be done before writing image data.
+// Image data must be transformed to use the indices assigned within the palette.
+static void writePalette(png_structp writePtr, png_infop writeInfoPtr,
+                  std::unordered_map<uint32_t, int>* colorPalette,
+                  std::unordered_set<uint32_t>* alphaPalette) {
+    assert(colorPalette->size() <= 256);
+    assert(alphaPalette->size() <= 256);
+
+    // Populate the PNG palette struct and assign indices to the color
+    // palette.
+
+    // Colors in the alpha palette should have smaller indices.
+    // This will ensure that we can truncate the alpha palette if it is
+    // smaller than the color palette.
+    int index = 0;
+    for (uint32_t color : *alphaPalette) {
+        (*colorPalette)[color] = index++;
+    }
+
+    // Assign the rest of the entries.
+    for (auto& entry : *colorPalette) {
+        if (entry.second == -1) {
+            entry.second = index++;
+        }
+    }
+
+    // Create the PNG color palette struct.
+    auto colorPaletteBytes = std::unique_ptr<png_color[]>(new png_color[colorPalette->size()]);
+
+    std::unique_ptr<png_byte[]> alphaPaletteBytes;
+    if (!alphaPalette->empty()) {
+        alphaPaletteBytes = std::unique_ptr<png_byte[]>(new png_byte[alphaPalette->size()]);
+    }
+
+    for (const auto& entry : *colorPalette) {
+        const uint32_t color = entry.first;
+        const int index = entry.second;
+        assert(index >= 0);
+        assert(static_cast<size_t>(index) < colorPalette->size());
+
+        png_colorp slot = colorPaletteBytes.get() + index;
+        slot->red = color >> 24;
+        slot->green = color >> 16;
+        slot->blue = color >> 8;
+
+        const png_byte alpha = color & 0x000000ff;
+        if (alpha != 0xff && alphaPaletteBytes) {
+            assert(static_cast<size_t>(index) < alphaPalette->size());
+            alphaPaletteBytes[index] = alpha;
+        }
+    }
+
+    // The bytes get copied here, so it is safe to release colorPaletteBytes at the end of function
+    // scope.
+    png_set_PLTE(writePtr, writeInfoPtr, colorPaletteBytes.get(), colorPalette->size());
+
+    if (alphaPaletteBytes) {
+        png_set_tRNS(writePtr, writeInfoPtr, alphaPaletteBytes.get(), alphaPalette->size(),
+                     nullptr);
+    }
+}
+
+// Write the 9-patch custom PNG chunks to writeInfoPtr. This must be done before
+// writing image data.
+static void writeNinePatch(png_structp writePtr, png_infop writeInfoPtr,
+                           const NinePatch* ninePatch) {
+    // The order of the chunks is important.
+    // 9-patch code in older platforms expects the 9-patch chunk to
+    // be last.
+
+    png_unknown_chunk unknownChunks[3];
+    memset(unknownChunks, 0, sizeof(unknownChunks));
+
+    size_t index = 0;
+    size_t chunkLen = 0;
+
+    std::unique_ptr<uint8_t[]> serializedOutline =
+            ninePatch->serializeRoundedRectOutline(&chunkLen);
+    strcpy((char*) unknownChunks[index].name, "npOl");
+    unknownChunks[index].size = chunkLen;
+    unknownChunks[index].data = (png_bytep) serializedOutline.get();
+    unknownChunks[index].location = PNG_HAVE_PLTE;
+    index++;
+
+    std::unique_ptr<uint8_t[]> serializedLayoutBounds;
+    if (ninePatch->layoutBounds.nonZero()) {
+        serializedLayoutBounds = ninePatch->serializeLayoutBounds(&chunkLen);
+        strcpy((char*) unknownChunks[index].name, "npLb");
+        unknownChunks[index].size = chunkLen;
+        unknownChunks[index].data = (png_bytep) serializedLayoutBounds.get();
+        unknownChunks[index].location = PNG_HAVE_PLTE;
+        index++;
+    }
+
+    std::unique_ptr<uint8_t[]> serializedNinePatch = ninePatch->serializeBase(&chunkLen);
+    strcpy((char*) unknownChunks[index].name, "npTc");
+    unknownChunks[index].size = chunkLen;
+    unknownChunks[index].data = (png_bytep) serializedNinePatch.get();
+    unknownChunks[index].location = PNG_HAVE_PLTE;
+    index++;
+
+    // Handle all unknown chunks. We are manually setting the chunks here,
+    // so we will only ever handle our custom chunks.
+    png_set_keep_unknown_chunks(writePtr, PNG_HANDLE_CHUNK_ALWAYS, nullptr, 0);
+
+    // Set the actual chunks here. The data gets copied, so our buffers can
+    // safely go out of scope.
+    png_set_unknown_chunks(writePtr, writeInfoPtr, unknownChunks, index);
+}
+
+bool writePng(IAaptContext* context, const Image* image, const NinePatch* ninePatch,
+              io::OutputStream* out, const PngOptions& options) {
+    // Create and initialize the write png_struct with the default error and warning handlers.
+    // The header version is also passed in to ensure that this was built against the same
+    // version of libpng.
+    png_structp writePtr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+                                                   nullptr, nullptr, nullptr);
+    if (writePtr == nullptr) {
+        context->getDiagnostics()->error(DiagMessage()
+                                         << "failed to create libpng write png_struct");
+        return false;
+    }
+
+    // Allocate memory to store image header data.
+    png_infop writeInfoPtr = png_create_info_struct(writePtr);
+    if (writeInfoPtr == nullptr) {
+        context->getDiagnostics()->error(DiagMessage() << "failed to create libpng write png_info");
+        png_destroy_write_struct(&writePtr, nullptr);
+        return false;
+    }
+
+    // Automatically release PNG resources at end of scope.
+    PngWriteStructDeleter pngWriteDeleter(writePtr, writeInfoPtr);
+
+    // libpng uses longjmp to jump to error handling routines.
+    // setjmp will return true only if it was jumped to, aka, there was an error.
+    if (setjmp(png_jmpbuf(writePtr))) {
+        return false;
+    }
+
+    // Handle warnings with our IDiagnostics.
+    png_set_error_fn(writePtr, (png_voidp) context->getDiagnostics(), logError, logWarning);
+
+    // Set up the write functions which write to our custom data sources.
+    png_set_write_fn(writePtr, (png_voidp) out, writeDataToStream, nullptr);
+
+    // We want small files and can take the performance hit to achieve this goal.
+    png_set_compression_level(writePtr, Z_BEST_COMPRESSION);
+
+    // Begin analysis of the image data.
+    // Scan the entire image and determine if:
+    // 1. Every pixel has R == G == B (grayscale)
+    // 2. Every pixel has A == 255 (opaque)
+    // 3. There are no more than 256 distinct RGBA colors (palette).
+    std::unordered_map<uint32_t, int> colorPalette;
+    std::unordered_set<uint32_t> alphaPalette;
+    bool needsToZeroRGBChannelsOfTransparentPixels = false;
+    bool grayScale = true;
+    int maxGrayDeviation = 0;
+
+    for (int32_t y = 0; y < image->height; y++) {
+        const uint8_t* row = image->rows[y];
+        for (int32_t x = 0; x < image->width; x++) {
+            int red = *row++;
+            int green = *row++;
+            int blue = *row++;
+            int alpha = *row++;
+
+            if (alpha == 0) {
+                // The color is completely transparent.
+                // For purposes of palettes and grayscale optimization,
+                // treat all channels as 0x00.
+                needsToZeroRGBChannelsOfTransparentPixels =
+                        needsToZeroRGBChannelsOfTransparentPixels ||
+                        (red != 0 || green != 0 || blue != 0);
+                red = green = blue = 0;
+            }
+
+            // Insert the color into the color palette.
+            const uint32_t color = red << 24 | green << 16 | blue << 8 | alpha;
+            colorPalette[color] = -1;
+
+            // If the pixel has non-opaque alpha, insert it into the
+            // alpha palette.
+            if (alpha != 0xff) {
+                alphaPalette.insert(color);
+            }
+
+            // Check if the image is indeed grayscale.
+            if (grayScale) {
+                if (red != green || red != blue) {
+                    grayScale = false;
+                }
+            }
+
+            // Calculate the gray scale deviation so that it can be compared
+            // with the threshold.
+            maxGrayDeviation = std::max(std::abs(red - green), maxGrayDeviation);
+            maxGrayDeviation = std::max(std::abs(green - blue), maxGrayDeviation);
+            maxGrayDeviation = std::max(std::abs(blue - red), maxGrayDeviation);
+        }
+    }
+
+    if (context->verbose()) {
+        DiagMessage msg;
+        msg << " paletteSize=" << colorPalette.size()
+                << " alphaPaletteSize=" << alphaPalette.size()
+                << " maxGrayDeviation=" << maxGrayDeviation
+                << " grayScale=" << (grayScale ? "true" : "false");
+        context->getDiagnostics()->note(msg);
+    }
+
+    const bool convertibleToGrayScale = maxGrayDeviation <= options.grayScaleTolerance;
+
+    const int newColorType = pickColorType(image->width, image->height, grayScale,
+                                           convertibleToGrayScale, ninePatch != nullptr,
+                                           colorPalette.size(), alphaPalette.size());
+
+    if (context->verbose()) {
+        DiagMessage msg;
+        msg << "encoding PNG ";
+        if (ninePatch) {
+            msg << "(with 9-patch) as ";
+        }
+        switch (newColorType) {
+        case PNG_COLOR_TYPE_GRAY:
+            msg << "GRAY";
+            break;
+        case PNG_COLOR_TYPE_GRAY_ALPHA:
+            msg << "GRAY + ALPHA";
+            break;
+        case PNG_COLOR_TYPE_RGB:
+            msg << "RGB";
+            break;
+        case PNG_COLOR_TYPE_RGB_ALPHA:
+            msg << "RGBA";
+            break;
+        case PNG_COLOR_TYPE_PALETTE:
+            msg << "PALETTE";
+            break;
+        default:
+            msg << "unknown type " << newColorType;
+            break;
+        }
+        context->getDiagnostics()->note(msg);
+    }
+
+    png_set_IHDR(writePtr, writeInfoPtr, image->width, image->height, 8, newColorType,
+                 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+    if (newColorType & PNG_COLOR_MASK_PALETTE) {
+        // Assigns indices to the palette, and writes the encoded palette to the libpng writePtr.
+        writePalette(writePtr, writeInfoPtr, &colorPalette, &alphaPalette);
+        png_set_filter(writePtr, 0, PNG_NO_FILTERS);
+    } else {
+        png_set_filter(writePtr, 0, PNG_ALL_FILTERS);
+    }
+
+    if (ninePatch) {
+        writeNinePatch(writePtr, writeInfoPtr, ninePatch);
+    }
+
+    // Flush our updates to the header.
+    png_write_info(writePtr, writeInfoPtr);
+
+    // Write out each row of image data according to its encoding.
+    if (newColorType == PNG_COLOR_TYPE_PALETTE) {
+        // 1 byte/pixel.
+        auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width]);
+
+        for (int32_t y = 0; y < image->height; y++) {
+            png_const_bytep inRow = image->rows[y];
+            for (int32_t x = 0; x < image->width; x++) {
+                int rr = *inRow++;
+                int gg = *inRow++;
+                int bb = *inRow++;
+                int aa = *inRow++;
+                if (aa == 0) {
+                    // Zero out color channels when transparent.
+                    rr = gg = bb = 0;
+                }
+
+                const uint32_t color = rr << 24 | gg << 16 | bb << 8 | aa;
+                const int idx = colorPalette[color];
+                assert(idx != -1);
+                outRow[x] = static_cast<png_byte>(idx);
+            }
+            png_write_row(writePtr, outRow.get());
+        }
+    } else if (newColorType == PNG_COLOR_TYPE_GRAY || newColorType == PNG_COLOR_TYPE_GRAY_ALPHA) {
+        const size_t bpp = newColorType == PNG_COLOR_TYPE_GRAY ? 1 : 2;
+        auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
+
+        for (int32_t y = 0; y < image->height; y++) {
+            png_const_bytep inRow = image->rows[y];
+            for (int32_t x = 0; x < image->width; x++) {
+                int rr = inRow[x * 4];
+                int gg = inRow[x * 4 + 1];
+                int bb = inRow[x * 4 + 2];
+                int aa = inRow[x * 4 + 3];
+                if (aa == 0) {
+                    // Zero out the gray channel when transparent.
+                    rr = gg = bb = 0;
+                }
+
+                if (grayScale) {
+                    // The image was already grayscale, red == green == blue.
+                    outRow[x * bpp] = inRow[x * 4];
+                } else {
+                    // The image is convertible to grayscale, use linear-luminance of
+                    // sRGB colorspace: https://en.wikipedia.org/wiki/Grayscale#Colorimetric_.28luminance-preserving.29_conversion_to_grayscale
+                    outRow[x * bpp] = (png_byte) (rr * 0.2126f + gg * 0.7152f + bb * 0.0722f);
+                }
+
+                if (bpp == 2) {
+                    // Write out alpha if we have it.
+                    outRow[x * bpp + 1] = aa;
+                }
+            }
+            png_write_row(writePtr, outRow.get());
+        }
+    } else if (newColorType == PNG_COLOR_TYPE_RGB || newColorType == PNG_COLOR_TYPE_RGBA) {
+        const size_t bpp = newColorType == PNG_COLOR_TYPE_RGB ? 3 : 4;
+        if (needsToZeroRGBChannelsOfTransparentPixels) {
+            // The source RGBA data can't be used as-is, because we need to zero out the RGB
+            // values of transparent pixels.
+            auto outRow = std::unique_ptr<png_byte[]>(new png_byte[image->width * bpp]);
+
+            for (int32_t y = 0; y < image->height; y++) {
+                png_const_bytep inRow = image->rows[y];
+                for (int32_t x = 0; x < image->width; x++) {
+                    int rr = *inRow++;
+                    int gg = *inRow++;
+                    int bb = *inRow++;
+                    int aa = *inRow++;
+                    if (aa == 0) {
+                        // Zero out the RGB channels when transparent.
+                        rr = gg = bb = 0;
+                    }
+                    outRow[x * bpp] = rr;
+                    outRow[x * bpp + 1] = gg;
+                    outRow[x * bpp + 2] = bb;
+                    if (bpp == 4) {
+                        outRow[x * bpp + 3] = aa;
+                    }
+                }
+                png_write_row(writePtr, outRow.get());
+            }
+        } else {
+            // The source image can be used as-is, just tell libpng whether or not to ignore
+            // the alpha channel.
+            if (newColorType == PNG_COLOR_TYPE_RGB) {
+                // Delete the extraneous alpha values that we appended to our buffer
+                // when reading the original values.
+                png_set_filler(writePtr, 0, PNG_FILLER_AFTER);
+            }
+            png_write_image(writePtr, image->rows.get());
+        }
+    } else {
+        assert(false && "unreachable");
+    }
+
+    png_write_end(writePtr, writeInfoPtr);
+    return true;
+}
+
+} // namespace aapt
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/cheap_transparency.png b/tools/aapt2/integration-tests/AppOne/res/drawable/cheap_transparency.png
new file mode 100644
index 0000000..0522a99
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/cheap_transparency.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/complex.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/complex.9.png
new file mode 100644
index 0000000..baf9fff
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/complex.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/outline_8x8.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/outline_8x8.9.png
new file mode 100644
index 0000000..7b331e1
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/outline_8x8.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_off_center_outline_32x16.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_off_center_outline_32x16.9.png
new file mode 100644
index 0000000..0ec6c70
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_off_center_outline_32x16.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_outline_32x16.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_outline_32x16.9.png
new file mode 100644
index 0000000..e05708a
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/round_rect_outline_32x16.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_3x3.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_3x3.9.png
new file mode 100644
index 0000000..a11377a
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_optical_bounds_3x3.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_optical_bounds_3x3.9.png
new file mode 100644
index 0000000..6803e42
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/transparent_optical_bounds_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/white_3x3.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/white_3x3.9.png
new file mode 100644
index 0000000..1a3731b
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/white_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/integration-tests/AppOne/res/drawable/white_optical_bounds_3x3.9.png b/tools/aapt2/integration-tests/AppOne/res/drawable/white_optical_bounds_3x3.9.png
new file mode 100644
index 0000000..489ace2
--- /dev/null
+++ b/tools/aapt2/integration-tests/AppOne/res/drawable/white_optical_bounds_3x3.9.png
Binary files differ
diff --git a/tools/aapt2/io/Io.cpp b/tools/aapt2/io/Io.cpp
new file mode 100644
index 0000000..963c21c
--- /dev/null
+++ b/tools/aapt2/io/Io.cpp
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#include "io/Io.h"
+
+#include <algorithm>
+#include <cstring>
+
+namespace aapt {
+namespace io {
+
+bool copy(OutputStream* out, InputStream* in) {
+    const void* inBuffer;
+    int inLen;
+    while (in->Next(&inBuffer, &inLen)) {
+        void* outBuffer;
+        int outLen;
+        if (!out->Next(&outBuffer, &outLen)) {
+            return !out->HadError();
+        }
+
+        const int bytesToCopy = std::min(inLen, outLen);
+        memcpy(outBuffer, inBuffer, bytesToCopy);
+        out->BackUp(outLen - bytesToCopy);
+        in->BackUp(inLen - bytesToCopy);
+    }
+    return !in->HadError();
+}
+
+} // namespace io
+} // namespace aapt
diff --git a/tools/aapt2/io/Io.h b/tools/aapt2/io/Io.h
new file mode 100644
index 0000000..e1e9107
--- /dev/null
+++ b/tools/aapt2/io/Io.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_IO_IO_H
+#define AAPT_IO_IO_H
+
+#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
+#include <string>
+
+namespace aapt {
+namespace io {
+
+/**
+ * InputStream interface that inherits from protobuf's ZeroCopyInputStream,
+ * but adds error handling methods to better report issues.
+ *
+ * The code style here matches the protobuf style.
+ */
+class InputStream : public google::protobuf::io::ZeroCopyInputStream {
+public:
+    virtual std::string GetError() const {
+        return {};
+    }
+
+    virtual bool HadError() const = 0;
+};
+
+/**
+ * OutputStream interface that inherits from protobuf's ZeroCopyOutputStream,
+ * but adds error handling methods to better report issues.
+ *
+ * The code style here matches the protobuf style.
+ */
+class OutputStream : public google::protobuf::io::ZeroCopyOutputStream {
+public:
+    virtual std::string GetError() const {
+        return {};
+    }
+
+    virtual bool HadError() const = 0;
+};
+
+/**
+ * Copies the data from in to out. Returns true if there was no error.
+ * If there was an error, check the individual streams' HadError/GetError
+ * methods.
+ */
+bool copy(OutputStream* out, InputStream* in);
+
+} // namespace io
+} // namespace aapt
+
+#endif /* AAPT_IO_IO_H */
diff --git a/tools/aapt2/link/Link.cpp b/tools/aapt2/link/Link.cpp
index c236394..b6b4b473 100644
--- a/tools/aapt2/link/Link.cpp
+++ b/tools/aapt2/link/Link.cpp
@@ -72,6 +72,7 @@
 
     bool noAutoVersion = false;
     bool noVersionVectors = false;
+    bool noResourceDeduping = false;
     bool staticLib = false;
     bool noStaticLibPackages = false;
     bool generateNonFinalIds = false;
@@ -1505,6 +1506,14 @@
             }
         }
 
+        if (!mOptions.noResourceDeduping) {
+            ResourceDeduper deduper;
+            if (!deduper.consume(mContext, &mFinalTable)) {
+                mContext->getDiagnostics()->error(DiagMessage() << "failed deduping resources");
+                return 1;
+            }
+        }
+
         proguard::KeepSet proguardKeepSet;
         proguard::KeepSet proguardMainDexKeepSet;
 
@@ -1743,6 +1752,9 @@
                             "Disables automatic versioning of vector drawables. Use this only\n"
                             "when building with vector drawable support library",
                             &options.noVersionVectors)
+            .optionalSwitch("--no-resource-deduping", "Disables automatic deduping of resources with\n"
+                            "identical values across compatible configurations.",
+                            &options.noResourceDeduping)
             .optionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01",
                             &legacyXFlag)
             .optionalSwitch("-z", "Require localization of strings marked 'suggested'",
diff --git a/tools/aapt2/link/Linkers.h b/tools/aapt2/link/Linkers.h
index 82e2868..ce455da 100644
--- a/tools/aapt2/link/Linkers.h
+++ b/tools/aapt2/link/Linkers.h
@@ -60,6 +60,14 @@
 };
 
 /**
+ * Removes duplicated key-value entries from dominated resources.
+ */
+class ResourceDeduper : public IResourceTableConsumer {
+public:
+    bool consume(IAaptContext* context, ResourceTable* table) override;
+};
+
+/**
  * If any attribute resource values are defined as public, this consumer will move all private
  * attribute resource values to a private ^private-attr type, avoiding backwards compatibility
  * issues with new apps running on old platforms.
diff --git a/tools/aapt2/link/ResourceDeduper.cpp b/tools/aapt2/link/ResourceDeduper.cpp
new file mode 100644
index 0000000..0276261
--- /dev/null
+++ b/tools/aapt2/link/ResourceDeduper.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+#include "DominatorTree.h"
+#include "ResourceTable.h"
+#include "link/Linkers.h"
+
+#include <algorithm>
+
+namespace aapt {
+
+namespace {
+
+/**
+ * Remove duplicated key-value entries from dominated resources.
+ *
+ * Based on the dominator tree, we can remove a value of an entry if:
+ *
+ * 1. The configuration for the entry's value is dominated by a configuration
+ *    with an equivalent entry value.
+ * 2. All compatible configurations for the entry (those not in conflict and
+ *    unrelated by domination with the configuration for the entry's value) have
+ *    an equivalent entry value.
+ */
+class DominatedKeyValueRemover : public DominatorTree::BottomUpVisitor {
+public:
+    using Node = DominatorTree::Node;
+
+    explicit DominatedKeyValueRemover(IAaptContext* context, ResourceEntry* entry) :
+            mContext(context), mEntry(entry) {
+    }
+
+    void visitConfig(Node* node) {
+        Node* parent = node->parent();
+        if (!parent) {
+            return;
+        }
+        ResourceConfigValue* nodeValue = node->value();
+        ResourceConfigValue* parentValue = parent->value();
+        if (!nodeValue || !parentValue) {
+            return;
+        }
+        if (!nodeValue->value->equals(parentValue->value.get())) {
+            return;
+        }
+
+        // Compare compatible configs for this entry and ensure the values are
+        // equivalent.
+        const ConfigDescription& nodeConfiguration = nodeValue->config;
+        for (const auto& sibling : mEntry->values) {
+            if (!sibling->value) {
+                // Sibling was already removed.
+                continue;
+            }
+            if (nodeConfiguration.isCompatibleWith(sibling->config)
+                    && !nodeValue->value->equals(sibling->value.get())) {
+                // The configurations are compatible, but the value is
+                // different, so we can't remove this value.
+                return;
+            }
+        }
+        if (mContext->verbose()) {
+            mContext->getDiagnostics()->note(
+                    DiagMessage(nodeValue->value->getSource())
+                            << "removing dominated duplicate resource with name \""
+                            << mEntry->name << "\"");
+        }
+        nodeValue->value = {};
+    }
+
+private:
+    IAaptContext* mContext;
+    ResourceEntry* mEntry;
+};
+
+static void dedupeEntry(IAaptContext* context, ResourceEntry* entry) {
+    DominatorTree tree(entry->values);
+    DominatedKeyValueRemover remover(context, entry);
+    tree.accept(&remover);
+
+    // Erase the values that were removed.
+    entry->values.erase(std::remove_if(entry->values.begin(), entry->values.end(),
+            [](const std::unique_ptr<ResourceConfigValue>& val) -> bool {
+        return val == nullptr || val->value == nullptr;
+    }), entry->values.end());
+}
+
+} // namespace
+
+bool ResourceDeduper::consume(IAaptContext* context, ResourceTable* table) {
+    for (auto& package : table->packages) {
+        for (auto& type : package->types) {
+            for (auto& entry : type->entries) {
+                dedupeEntry(context, entry.get());
+            }
+        }
+    }
+    return true;
+}
+
+} // aapt
diff --git a/tools/aapt2/link/ResourceDeduper_test.cpp b/tools/aapt2/link/ResourceDeduper_test.cpp
new file mode 100644
index 0000000..47071a51
--- /dev/null
+++ b/tools/aapt2/link/ResourceDeduper_test.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#include "ResourceTable.h"
+#include "link/Linkers.h"
+#include "test/Test.h"
+
+namespace aapt {
+
+TEST(ResourceDeduperTest, SameValuesAreDeduped) {
+    std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+    const ConfigDescription defaultConfig = {};
+    const ConfigDescription enConfig = test::parseConfigOrDie("en");
+    const ConfigDescription enV21Config = test::parseConfigOrDie("en-v21");
+    // Chosen because this configuration is compatible with en.
+    const ConfigDescription landConfig = test::parseConfigOrDie("land");
+
+    std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+            .addString("android:string/dedupe", ResourceId{}, defaultConfig, "dedupe")
+            .addString("android:string/dedupe", ResourceId{}, enConfig, "dedupe")
+            .addString("android:string/dedupe", ResourceId{}, landConfig, "dedupe")
+            .addString("android:string/dedupe2", ResourceId{}, defaultConfig, "dedupe")
+            .addString("android:string/dedupe2", ResourceId{}, enConfig, "dedupe")
+            .addString("android:string/dedupe2", ResourceId{}, enV21Config, "keep")
+            .addString("android:string/dedupe2", ResourceId{}, landConfig, "dedupe")
+            .build();
+
+    ASSERT_TRUE(ResourceDeduper().consume(context.get(), table.get()));
+    EXPECT_EQ(
+            nullptr,
+            test::getValueForConfig<String>(table.get(), "android:string/dedupe", enConfig));
+    EXPECT_EQ(
+            nullptr,
+            test::getValueForConfig<String>(table.get(), "android:string/dedupe", landConfig));
+    EXPECT_EQ(
+            nullptr,
+            test::getValueForConfig<String>(table.get(), "android:string/dedupe2", enConfig));
+    EXPECT_NE(
+            nullptr,
+            test::getValueForConfig<String>(table.get(), "android:string/dedupe2", enV21Config));
+}
+
+TEST(ResourceDeduperTest, DifferentValuesAreKept) {
+    std::unique_ptr<IAaptContext> context = test::ContextBuilder().build();
+    const ConfigDescription defaultConfig = {};
+    const ConfigDescription enConfig = test::parseConfigOrDie("en");
+    const ConfigDescription enV21Config = test::parseConfigOrDie("en-v21");
+    // Chosen because this configuration is compatible with en.
+    const ConfigDescription landConfig = test::parseConfigOrDie("land");
+
+    std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder()
+            .addString("android:string/keep", ResourceId{}, defaultConfig, "keep")
+            .addString("android:string/keep", ResourceId{}, enConfig, "keep")
+            .addString("android:string/keep", ResourceId{}, enV21Config, "keep2")
+            .addString("android:string/keep", ResourceId{}, landConfig, "keep2")
+            .build();
+
+    ASSERT_TRUE(ResourceDeduper().consume(context.get(), table.get()));
+    EXPECT_NE(
+            nullptr,
+            test::getValueForConfig<String>(table.get(), "android:string/keep", enConfig));
+    EXPECT_NE(
+            nullptr,
+            test::getValueForConfig<String>(table.get(), "android:string/keep", enV21Config));
+    EXPECT_NE(
+            nullptr,
+            test::getValueForConfig<String>(table.get(), "android:string/keep", landConfig));
+}
+
+}  // namespace aapt
diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md
index 267200d..93c790d 100644
--- a/tools/aapt2/readme.md
+++ b/tools/aapt2/readme.md
@@ -4,6 +4,10 @@
 ### `aapt2 compile ...`
 - Added support for inline complex XML resources. See
   https://developer.android.com/guide/topics/resources/complex-xml-resources.html
+### `aapt link ...`
+- Duplicate resource filtering: removes duplicate resources in dominated configurations
+  that are always identical when selected at runtime. This can be disabled with
+  `--no-resource-deduping`.
 
 ## Version 2.1
 ### `aapt2 link ...`
diff --git a/tools/aapt2/util/BigBuffer.cpp b/tools/aapt2/util/BigBuffer.cpp
index c88e3c1..de4ecd2 100644
--- a/tools/aapt2/util/BigBuffer.cpp
+++ b/tools/aapt2/util/BigBuffer.cpp
@@ -49,4 +49,29 @@
     return mBlocks.back().buffer.get();
 }
 
+void* BigBuffer::nextBlock(size_t* outSize) {
+    if (!mBlocks.empty()) {
+        Block& block = mBlocks.back();
+        if (block.size != block.mBlockSize) {
+            void* outBuffer = block.buffer.get() + block.size;
+            size_t size = block.mBlockSize - block.size;
+            block.size = block.mBlockSize;
+            mSize += size;
+            *outSize = size;
+            return outBuffer;
+        }
+    }
+
+    // Zero-allocate the block's buffer.
+    Block block = {};
+    block.buffer = std::unique_ptr<uint8_t[]>(new uint8_t[mBlockSize]());
+    assert(block.buffer);
+    block.size = mBlockSize;
+    block.mBlockSize = mBlockSize;
+    mBlocks.push_back(std::move(block));
+    mSize += mBlockSize;
+    *outSize = mBlockSize;
+    return mBlocks.back().buffer.get();
+}
+
 } // namespace aapt
diff --git a/tools/aapt2/util/BigBuffer.h b/tools/aapt2/util/BigBuffer.h
index ba8532f..685614f 100644
--- a/tools/aapt2/util/BigBuffer.h
+++ b/tools/aapt2/util/BigBuffer.h
@@ -82,6 +82,20 @@
     T* nextBlock(size_t count = 1);
 
     /**
+     * Returns the next block available and puts the size in outCount.
+     * This is useful for grabbing blocks where the size doesn't matter.
+     * Use backUp() to give back any bytes that were not used.
+     */
+    void* nextBlock(size_t* outCount);
+
+    /**
+     * Backs up count bytes. This must only be called after nextBlock()
+     * and can not be larger than sizeof(T) * count of the last nextBlock()
+     * call.
+     */
+    void backUp(size_t count);
+
+    /**
      * Moves the specified BigBuffer into this one. When this method
      * returns, buffer is empty.
      */
@@ -97,6 +111,8 @@
      */
     void align4();
 
+    size_t getBlockSize() const;
+
     const_iterator begin() const;
     const_iterator end() const;
 
@@ -123,6 +139,10 @@
     return mSize;
 }
 
+inline size_t BigBuffer::getBlockSize() const {
+    return mBlockSize;
+}
+
 template <typename T>
 inline T* BigBuffer::nextBlock(size_t count) {
     static_assert(std::is_standard_layout<T>::value, "T must be standard_layout type");
@@ -130,6 +150,12 @@
     return reinterpret_cast<T*>(nextBlockImpl(sizeof(T) * count));
 }
 
+inline void BigBuffer::backUp(size_t count) {
+    Block& block = mBlocks.back();
+    block.size -= count;
+    mSize -= count;
+}
+
 inline void BigBuffer::appendBuffer(BigBuffer&& buffer) {
     std::move(buffer.mBlocks.begin(), buffer.mBlocks.end(), std::back_inserter(mBlocks));
     mSize += buffer.mSize;
diff --git a/tools/aapt2/util/StringPiece.h b/tools/aapt2/util/StringPiece.h
index 4300a67..266c003 100644
--- a/tools/aapt2/util/StringPiece.h
+++ b/tools/aapt2/util/StringPiece.h
@@ -39,6 +39,9 @@
     using const_iterator = const TChar*;
     using difference_type = size_t;
 
+    // End of string marker.
+    constexpr static const size_t npos = static_cast<size_t>(-1);
+
     BasicStringPiece();
     BasicStringPiece(const BasicStringPiece<TChar>& str);
     BasicStringPiece(const std::basic_string<TChar>& str);  // NOLINT(implicit)
@@ -48,7 +51,7 @@
     BasicStringPiece<TChar>& operator=(const BasicStringPiece<TChar>& rhs);
     BasicStringPiece<TChar>& assign(const TChar* str, size_t len);
 
-    BasicStringPiece<TChar> substr(size_t start, size_t len) const;
+    BasicStringPiece<TChar> substr(size_t start, size_t len = npos) const;
     BasicStringPiece<TChar> substr(BasicStringPiece<TChar>::const_iterator begin,
                                    BasicStringPiece<TChar>::const_iterator end) const;
 
@@ -81,6 +84,9 @@
 //
 
 template <typename TChar>
+constexpr const size_t BasicStringPiece<TChar>::npos;
+
+template <typename TChar>
 inline BasicStringPiece<TChar>::BasicStringPiece() : mData(nullptr) , mLength(0) {
 }
 
@@ -127,7 +133,11 @@
 
 template <typename TChar>
 inline BasicStringPiece<TChar> BasicStringPiece<TChar>::substr(size_t start, size_t len) const {
-    if (start + len > mLength) {
+    if (len == npos) {
+        len = mLength - start;
+    }
+
+    if (start > mLength || start + len > mLength) {
         return BasicStringPiece<TChar>();
     }
     return BasicStringPiece<TChar>(mData + start, len);
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
index f1da3a2..bcfe3bf 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -627,7 +627,7 @@
         boolean isPremultiplied = createFlags.contains(BitmapCreateFlags.PREMULTIPLIED);
 
         // and create/return a new Bitmap with it
-        return new Bitmap(nativeInt, null /* buffer */, width, height, density, isMutable,
+        return new Bitmap(nativeInt, width, height, density, isMutable,
                           isPremultiplied, null /*ninePatchChunk*/, null /* layoutBounds */);
     }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 8d93b7f..a0b2977 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -76,6 +76,7 @@
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
+import android.os.ShellCallback;
 import android.os.UserHandle;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
@@ -1180,7 +1181,7 @@
 
                 @Override
                 public void shellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
-                  String[] args, ResultReceiver resultReceiver) {
+                  String[] args, ShellCallback shellCallback, ResultReceiver resultReceiver) {
                 }
             };
         }
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 67cf107..465addf 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -139,12 +139,6 @@
     public long seen;
 
     /**
-     * If the scan result is a valid autojoin candidate
-     * {@hide}
-     */
-    public int isAutoJoinCandidate;
-
-    /**
      * @hide
      * Update RSSI of the scan result
      * @param previousRssi
@@ -452,7 +446,6 @@
             numConnection = source.numConnection;
             numUsage = source.numUsage;
             numIpConfigFailures = source.numIpConfigFailures;
-            isAutoJoinCandidate = source.isAutoJoinCandidate;
             venueName = source.venueName;
             operatorFriendlyName = source.operatorFriendlyName;
             flags = source.flags;
@@ -530,7 +523,6 @@
         dest.writeInt(numConnection);
         dest.writeInt(numUsage);
         dest.writeInt(numIpConfigFailures);
-        dest.writeInt(isAutoJoinCandidate);
         dest.writeString((venueName != null) ? venueName.toString() : "");
         dest.writeString((operatorFriendlyName != null) ? operatorFriendlyName.toString() : "");
         dest.writeLong(this.flags);
@@ -600,7 +592,6 @@
                 sr.numConnection = in.readInt();
                 sr.numUsage = in.readInt();
                 sr.numIpConfigFailures = in.readInt();
-                sr.isAutoJoinCandidate = in.readInt();
                 sr.venueName = in.readString();
                 sr.operatorFriendlyName = in.readString();
                 sr.flags = in.readLong();
diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/nan/WifiNanManager.java
index 705ba4a..bb15434 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanManager.java
+++ b/wifi/java/android/net/wifi/nan/WifiNanManager.java
@@ -201,6 +201,8 @@
      * Use the {@link #isAvailable()} to query the current status.
      * This broadcast is <b>not</b> sticky, use the {@link #isAvailable()} API after registering
      * the broadcast to check the current state of Wi-Fi NAN.
+     * <p>Note: The broadcast is only delivered to registered receivers - no manifest registered
+     * components will be launched.
      */
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_WIFI_NAN_STATE_CHANGED =