Merge "Drop fastjni from nSupportsOpenGL"
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/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..7e8cc0b 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/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/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_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/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-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/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 85838ed..3d1acdf5 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2274,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/symbols.xml b/core/res/res/values/symbols.xml
index 62997f7..019a5e8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2266,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/docs/html/wear/preview/downloads.jd b/docs/html/wear/preview/downloads.jd
index 83a3f98..da6a06d 100644
--- a/docs/html/wear/preview/downloads.jd
+++ b/docs/html/wear/preview/downloads.jd
@@ -653,6 +653,12 @@
       "{@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>
 
@@ -696,7 +702,9 @@
     </p>
 
     <ol>
-      <li>On the phone, install the Android Wear app from Google Play.
+      <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.
@@ -740,7 +748,7 @@
       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">
+      "{@docRoot}studio/run/managing-avds.html">
       Create and Manage Virtual Devices</a>.
     </p>
 
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/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 171532b..4a42e2b 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -1837,6 +1837,7 @@
 
 Asset* AssetManager::SharedZip::getResourceTableAsset()
 {
+    AutoMutex _l(gLock);
     ALOGV("Getting from SharedZip %p resource asset %p\n", this, mResourceTableAsset);
     return mResourceTableAsset;
 }
@@ -1846,10 +1847,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;
         }
     }
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
index 88b566a..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;
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 971f734..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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 9216d85..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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 99875c3..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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-be-rBY/strings.xml b/packages/SettingsLib/res/values-be-rBY/strings.xml
index 5c2d860..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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 e4e2eef..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index ca3ffb4..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index d3873cd..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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 d3873cd..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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 d3873cd..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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-et-rEE/strings.xml b/packages/SettingsLib/res/values-et-rEE/strings.xml
index 72c71d5..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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 f641a56..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index e4cd51f..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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 063184a..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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-hy-rAM/strings.xml b/packages/SettingsLib/res/values-hy-rAM/strings.xml
index c4421f9..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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 24d9d7a..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 6a1bf32..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 4ee077f..4c4eed0 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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 cac3c23..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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 892c330..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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 ea1c964..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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 187c886..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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-mk-rMK/strings.xml b/packages/SettingsLib/res/values-mk-rMK/strings.xml
index a5c9ff2..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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-mn-rMN/strings.xml b/packages/SettingsLib/res/values-mn-rMN/strings.xml
index 9746ca2..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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-my-rMM/strings.xml b/packages/SettingsLib/res/values-my-rMM/strings.xml
index 92ab9b8..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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-ne-rNP/strings.xml b/packages/SettingsLib/res/values-ne-rNP/strings.xml
index 34ede3c..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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 0f19008..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 1dbc475..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 77a580e..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 9ed9459..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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 124a101..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 5409801..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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 482fa1a..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 030cfdd..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 7d7e396..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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 7ca5aaf..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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 67c0687..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 (5724903347310541706) -->
-    <skip />
-    <!-- no translation found for wifi_no_internet (3880396223819116454) -->
-    <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/src/com/android/settingslib/drawer/CategoryManager.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
index a8f286d..812d33a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryManager.java
@@ -5,7 +5,7 @@
  * 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,
@@ -21,8 +21,6 @@
 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;
@@ -33,8 +31,6 @@
 
     private static CategoryManager sInstance;
 
-    private final InterestingConfigChanges mInterestingConfigChanges;
-
     // Tile cache (key: <packageName, activityName>, value: tile)
     private final Map<Pair<String, String>, Tile> mTileByComponentCache;
 
@@ -51,12 +47,11 @@
     }
 
     CategoryManager() {
-        mInterestingConfigChanges = new InterestingConfigChanges();
         mTileByComponentCache = new ArrayMap<>();
         mCategoryByKeyMap = new ArrayMap<>();
     }
 
-    public DashboardCategory getTilesByCategory(Context context, String categoryKey) {
+    public synchronized DashboardCategory getTilesByCategory(Context context, String categoryKey) {
         tryInitCategories(context);
 
         final DashboardCategory category = mCategoryByKeyMap.get(categoryKey);
@@ -66,19 +61,17 @@
         return category;
     }
 
-    public List<DashboardCategory> getCategories(Context context) {
+    public synchronized List<DashboardCategory> getCategories(Context context) {
         tryInitCategories(context);
         return mCategories;
     }
 
-    public void reloadAllCategoriesForConfigChange(Context context) {
-        if (mInterestingConfigChanges.applyNewConfig(context.getResources())) {
-            mCategories = null;
-            tryInitCategories(context);
-        }
+    public synchronized void reloadAllCategories(Context context) {
+        mCategories = null;
+        tryInitCategories(context);
     }
 
-    public void updateCategoryFromBlacklist(Set<ComponentName> tileBlacklist) {
+    public synchronized void updateCategoryFromBlacklist(Set<ComponentName> tileBlacklist) {
         if (mCategories == null) {
             Log.w(TAG, "Category is null, skipping blacklist update");
         }
@@ -93,7 +86,7 @@
         }
     }
 
-    private void tryInitCategories(Context context) {
+    private synchronized void tryInitCategories(Context context) {
         if (mCategories == null) {
             mTileByComponentCache.clear();
             mCategoryByKeyMap.clear();
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 50867eb..bb6c558 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -434,7 +434,7 @@
 
         @Override
         protected Void doInBackground(Void... params) {
-            mCategoryManager.reloadAllCategoriesForConfigChange(SettingsDrawerActivity.this);
+            mCategoryManager.reloadAllCategories(SettingsDrawerActivity.this);
             return null;
         }
 
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/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 3dfcfe7..508cf24 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -368,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
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cf360ba..aa4d7be3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1302,7 +1302,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.
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 6c7d136..dc83ac0 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -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/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java b/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java
index da4d150..e4473ad 100644
--- a/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/retaildemo/RetailDemoModeServiceTest.java
@@ -147,6 +147,7 @@
         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));
@@ -157,7 +158,6 @@
         assertTrue("Not registered for " + Intent.ACTION_SCREEN_OFF,
                 intentFilter.getValue().hasAction(Intent.ACTION_SCREEN_OFF));
 
-        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);
diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk
index 18a1fbf..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,6 +37,7 @@
 	flatten/XmlFlattener.cpp \
 	io/File.cpp \
 	io/FileSystem.cpp \
+	io/Io.cpp \
 	io/ZipArchive.cpp \
 	link/AutoVersioner.cpp \
 	link/ManifestFixer.cpp \
@@ -84,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 \
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..408ecf7
--- /dev/null
+++ b/tools/aapt2/compile/NinePatch.cpp
@@ -0,0 +1,671 @@
+/*
+ * 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());
+    *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 << "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..ac4ee02
--- /dev/null
+++ b/tools/aapt2/compile/NinePatch_test.cpp
@@ -0,0 +1,322 @@
+/*
+ * 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,
+};
+
+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);
+}
+
+} // 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/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);