Merge "AAPT2: Fix typo in Config parsing"
diff --git a/Android.bp b/Android.bp
index eb35ffb..33acffa 100644
--- a/Android.bp
+++ b/Android.bp
@@ -40,6 +40,7 @@
             // needed by the device.
             srcs: [
                 "core/proto/android/os/kernelwake.proto",
+                "core/proto/android/os/procrank.proto",
                 "core/proto/android/service/graphicsstats.proto",
             ],
             shared: {
diff --git a/Android.mk b/Android.mk
index 35c50d5..1a79e0c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -172,6 +172,7 @@
 	core/java/android/content/pm/IPackageInstallerCallback.aidl \
 	core/java/android/content/pm/IPackageInstallerSession.aidl \
 	core/java/android/content/pm/IPackageManager.aidl \
+	../native/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl \
 	core/java/android/content/pm/IPackageMoveObserver.aidl \
 	core/java/android/content/pm/IPackageStatsObserver.aidl \
 	core/java/android/content/pm/IPinItemRequest.aidl \
@@ -602,8 +603,10 @@
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVA_LIBRARIES := core-oj core-libart conscrypt okhttp bouncycastle ext
 
-LOCAL_STATIC_JAVA_LIBRARIES :=                          \
-    framework-protos                                    \
+LOCAL_STATIC_JAVA_LIBRARIES :=                           \
+    framework-protos                                     \
+    android.hidl.base-V1.0-java                          \
+    android.hardware.cas-V1.0-java                       \
     android.hardware.health-V1.0-java-constants          \
     android.hardware.thermal-V1.0-java-constants         \
     android.hardware.tv.input-V1.0-java-constants        \
@@ -613,8 +616,6 @@
     android.hardware.vibrator-V1.1-java-constants        \
     android.hardware.wifi-V1.0-java-constants            \
 
-include hardware/interfaces/cas/1.0/CasHal.mk
-
 # Loaded with System.loadLibrary by android.view.textclassifier
 LOCAL_REQUIRED_MODULES += libtextclassifier
 
@@ -640,6 +641,32 @@
 
 framework_built := $(call java-lib-deps,framework)
 
+# HwBinder
+# =======================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+        core/java/android/os/HidlSupport.java \
+        core/java/android/annotation/NonNull.java \
+        core/java/android/os/HwBinder.java \
+        core/java/android/os/HwBlob.java \
+        core/java/android/os/HwParcel.java \
+        core/java/android/os/IHwBinder.java \
+        core/java/android/os/IHwInterface.java \
+        core/java/android/os/DeadObjectException.java \
+        core/java/android/os/DeadSystemException.java \
+        core/java/android/os/RemoteException.java \
+        core/java/android/util/AndroidException.java \
+
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_JAVA_LIBRARIES := core-oj core-libart
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := hwbinder
+
+LOCAL_DX_FLAGS := --core-library
+LOCAL_UNINSTALLABLE_MODULE := true
+include $(BUILD_JAVA_LIBRARY)
+
 # Copy AIDL files to be preprocessed and included in the SDK,
 # specified relative to the root of the build tree.
 # ============================================================
@@ -1016,7 +1043,7 @@
     -since $(SRC_API_DIR)/24.txt 24 \
     -since $(SRC_API_DIR)/25.txt 25 \
     -since $(SRC_API_DIR)/26.txt 26 \
-    -werror -hide 111 -hide 113 -hide 121 \
+    -werror -lerror -hide 111 -hide 113 -hide 121 -hide 125 -hide 126 -hide 127 -hide 128 \
     -overview $(LOCAL_PATH)/core/java/overview.html \
 
 framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \
diff --git a/apct-tests/perftests/core/src/android/widget/TextViewOnMeasurePerfTest.java b/apct-tests/perftests/core/src/android/widget/TextViewOnMeasurePerfTest.java
new file mode 100644
index 0000000..a14dd25
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/widget/TextViewOnMeasurePerfTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 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.widget;
+
+import static android.view.View.MeasureSpec.AT_MOST;
+import static android.view.View.MeasureSpec.EXACTLY;
+import static android.view.View.MeasureSpec.UNSPECIFIED;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+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 android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.TextAppearanceSpan;
+import android.view.LayoutInflater;
+
+import com.android.perftests.core.R;
+
+import java.util.Random;
+import java.util.Locale;
+
+import org.junit.Test;
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertTrue;
+
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class TextViewOnMeasurePerfTest {
+    private static final String MULTILINE_TEXT =
+        "Lorem ipsum dolor sit amet, \n"+
+        "consectetur adipiscing elit, \n" +
+        "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. \n" +
+        "Ut enim ad minim veniam, \n" +
+        "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.\n" +
+        "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat " +
+        "nulla pariatur.\n" +
+        "Excepteur sint occaecat cupidatat non proident, \n" +
+        "sunt in culpa qui officia deserunt mollit anim id est laborum.\n";
+
+    private static final int VIEW_WIDTH = 1000;
+    private static final int VIEW_HEIGHT = 1000;
+    private static final CharSequence COMPLEX_MULTILINE_TEXT;
+    static {
+        final SpannableStringBuilder ssb = new SpannableStringBuilder();
+
+        // To emphasize, append multiline text 10 times.
+        for (int i = 0; i < 10; ++i) {
+            ssb.append(MULTILINE_TEXT);
+        }
+
+        final ColorStateList[] COLORS = {
+                ColorStateList.valueOf(0xFFFF0000),  // RED
+                ColorStateList.valueOf(0xFF00FF00),  // GREEN
+                ColorStateList.valueOf(0xFF0000FF),  // BLUE
+        };
+
+        final int[] STYLES = {
+                Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC, Typeface.BOLD_ITALIC
+        };
+
+        final String[] FAMILIES = { "sans-serif", "serif", "monospace" };
+
+        // Append random span to text.
+        final Random random = new Random(0);
+        for (int pos = 0; pos < ssb.length();) {
+            final TextAppearanceSpan span = new TextAppearanceSpan(
+                FAMILIES[random.nextInt(FAMILIES.length)],
+                STYLES[random.nextInt(STYLES.length)],
+                24 + random.nextInt(32),  // text size. minimum 24
+                COLORS[random.nextInt(COLORS.length)],
+                COLORS[random.nextInt(COLORS.length)]);
+            int spanLength = 1 + random.nextInt(9);  // Up to 9 span length.
+            if (pos + spanLength > ssb.length()) {
+                spanLength = ssb.length() - pos;
+            }
+            ssb.setSpan(span, pos, pos + spanLength, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
+            pos += spanLength;
+        }
+        COMPLEX_MULTILINE_TEXT = ssb;
+    }
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void testMeasure_AtMost() throws Throwable {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        final TextView textView = new TextView(context);
+        textView.setText(COMPLEX_MULTILINE_TEXT);
+
+        while (state.keepRunning()) {
+            // Changing locale to invalidate internal layout.
+            textView.setTextLocale(Locale.UK);
+            textView.setTextLocale(Locale.US);
+
+            textView.measure(AT_MOST | VIEW_WIDTH, AT_MOST | VIEW_HEIGHT);
+        }
+    }
+
+    @Test
+    public void testMeasure_Exactly() throws Throwable {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        final TextView textView = new TextView(context);
+        textView.setText(COMPLEX_MULTILINE_TEXT);
+
+        while (state.keepRunning()) {
+            // Changing locale to invalidate internal layout.
+            textView.setTextLocale(Locale.UK);
+            textView.setTextLocale(Locale.US);
+
+            textView.measure(EXACTLY | VIEW_WIDTH, EXACTLY | VIEW_HEIGHT);
+        }
+    }
+
+    @Test
+    public void testMeasure_Unspecified() throws Throwable {
+        final Context context = InstrumentationRegistry.getTargetContext();
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+
+        final TextView textView = new TextView(context);
+        textView.setText(COMPLEX_MULTILINE_TEXT);
+
+        while (state.keepRunning()) {
+            // Changing locale to invalidate internal layout.
+            textView.setTextLocale(Locale.UK);
+            textView.setTextLocale(Locale.US);
+
+            textView.measure(UNSPECIFIED, UNSPECIFIED);
+        }
+    }
+}
diff --git a/api/current.txt b/api/current.txt
index b5c77e0..6f0c68a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -10506,6 +10506,7 @@
     method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
     method public void removeSplit(java.lang.String) throws java.io.IOException;
     method public void setStagingProgress(float);
+    method public void transfer(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
   }
 
   public static abstract class PackageInstaller.SessionCallback {
@@ -10523,10 +10524,16 @@
     method public android.graphics.Bitmap getAppIcon();
     method public java.lang.CharSequence getAppLabel();
     method public java.lang.String getAppPackageName();
+    method public int getInstallLocation();
     method public int getInstallReason();
     method public java.lang.String getInstallerPackageName();
+    method public int getMode();
+    method public int getOriginatingUid();
+    method public android.net.Uri getOriginatingUri();
     method public float getProgress();
+    method public android.net.Uri getReferrerUri();
     method public int getSessionId();
+    method public long getSize();
     method public boolean isActive();
     method public boolean isSealed();
     method public void writeToParcel(android.os.Parcel, int);
@@ -11902,7 +11909,7 @@
     method public boolean needUpgrade(int);
     method protected void onAllReferencesReleased();
     method public static android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int);
-    method public static android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.OpenParams);
+    method public static android.database.sqlite.SQLiteDatabase openDatabase(java.io.File, android.database.sqlite.SQLiteDatabase.OpenParams);
     method public static android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, android.database.DatabaseErrorHandler);
     method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.io.File, android.database.sqlite.SQLiteDatabase.CursorFactory);
     method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -13829,6 +13836,7 @@
   public class Typeface {
     method public static android.graphics.Typeface create(java.lang.String, int);
     method public static android.graphics.Typeface create(android.graphics.Typeface, int);
+    method public static android.graphics.Typeface create(android.graphics.Typeface, int, boolean);
     method public static android.graphics.Typeface createFromAsset(android.content.res.AssetManager, java.lang.String);
     method public static android.graphics.Typeface createFromFile(java.io.File);
     method public static android.graphics.Typeface createFromFile(java.lang.String);
@@ -15858,7 +15866,6 @@
     field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
     field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
     field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
-    field public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6; // 0x6
     field public static final int FINGERPRINT_ERROR_VENDOR = 8; // 0x8
   }
 
@@ -22742,6 +22749,7 @@
     method public android.graphics.Bitmap getFrameAtTime(long, int);
     method public android.graphics.Bitmap getFrameAtTime(long);
     method public android.graphics.Bitmap getFrameAtTime();
+    method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int);
     method public void release();
     method public void setDataSource(java.lang.String) throws java.lang.IllegalArgumentException;
     method public void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.lang.IllegalArgumentException;
@@ -37083,6 +37091,13 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.LuhnChecksumValidator> CREATOR;
   }
 
+  public final class RegexValidator implements android.os.Parcelable android.service.autofill.Validator {
+    ctor public RegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR;
+  }
+
   public final class SaveCallback {
     method public void onFailure(java.lang.CharSequence);
     method public void onSuccess();
@@ -37123,13 +37138,6 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
-  public final class SimpleRegexValidator implements android.os.Parcelable android.service.autofill.Validator {
-    ctor public SimpleRegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern);
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.SimpleRegexValidator> CREATOR;
-  }
-
   public abstract interface Transformation {
   }
 
@@ -41117,6 +41125,7 @@
     method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public java.lang.String getNameForUid(int);
+    method public java.lang.String[] getNamesForUids(int[]);
     method public int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public int[] getPackageGids(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -51645,7 +51654,9 @@
     method public android.graphics.Typeface getTypeface();
     method public android.text.style.URLSpan[] getUrls();
     method public boolean hasSelection();
+    method public boolean isAllCaps();
     method public boolean isCursorVisible();
+    method public boolean isElegantTextHeight();
     method public boolean isInputMethodTarget();
     method public boolean isSuggestionsEnabled();
     method public boolean isTextSelectable();
diff --git a/api/system-current.txt b/api/system-current.txt
index a277a6c..aca2dc2 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -11180,12 +11180,14 @@
     method public void abandon();
     method public void close();
     method public void commit(android.content.IntentSender);
+    method public void commitTransferred(android.content.IntentSender);
     method public void fsync(java.io.OutputStream) throws java.io.IOException;
     method public java.lang.String[] getNames() throws java.io.IOException;
     method public java.io.InputStream openRead(java.lang.String) throws java.io.IOException;
     method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
     method public void removeSplit(java.lang.String) throws java.io.IOException;
     method public void setStagingProgress(float);
+    method public void transfer(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
   }
 
   public static abstract class PackageInstaller.SessionCallback {
@@ -11200,13 +11202,26 @@
   public static class PackageInstaller.SessionInfo implements android.os.Parcelable {
     method public android.content.Intent createDetailsIntent();
     method public int describeContents();
+    method public boolean getAllocateAggressive();
+    method public boolean getAllowDowngrade();
     method public android.graphics.Bitmap getAppIcon();
     method public java.lang.CharSequence getAppLabel();
     method public java.lang.String getAppPackageName();
+    method public boolean getDontKillApp();
+    method public java.lang.String[] getGrantedRuntimePermissions();
+    method public boolean getInstallAsFullApp(boolean);
+    method public boolean getInstallAsInstantApp(boolean);
+    method public boolean getInstallAsVirtualPreload();
+    method public int getInstallLocation();
     method public int getInstallReason();
     method public java.lang.String getInstallerPackageName();
+    method public int getMode();
+    method public int getOriginatingUid();
+    method public android.net.Uri getOriginatingUri();
     method public float getProgress();
+    method public android.net.Uri getReferrerUri();
     method public int getSessionId();
+    method public long getSize();
     method public boolean isActive();
     method public boolean isSealed();
     method public void writeToParcel(android.os.Parcel, int);
@@ -12700,7 +12715,7 @@
     method public boolean needUpgrade(int);
     method protected void onAllReferencesReleased();
     method public static android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int);
-    method public static android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.OpenParams);
+    method public static android.database.sqlite.SQLiteDatabase openDatabase(java.io.File, android.database.sqlite.SQLiteDatabase.OpenParams);
     method public static android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, android.database.DatabaseErrorHandler);
     method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.io.File, android.database.sqlite.SQLiteDatabase.CursorFactory);
     method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -14627,6 +14642,7 @@
   public class Typeface {
     method public static android.graphics.Typeface create(java.lang.String, int);
     method public static android.graphics.Typeface create(android.graphics.Typeface, int);
+    method public static android.graphics.Typeface create(android.graphics.Typeface, int, boolean);
     method public static android.graphics.Typeface createFromAsset(android.content.res.AssetManager, java.lang.String);
     method public static android.graphics.Typeface createFromFile(java.io.File);
     method public static android.graphics.Typeface createFromFile(java.lang.String);
@@ -16675,7 +16691,6 @@
     field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
     field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
     field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
-    field public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6; // 0x6
     field public static final int FINGERPRINT_ERROR_VENDOR = 8; // 0x8
   }
 
@@ -17252,10 +17267,8 @@
     field public static final int IDENTIFIER_TYPE_RDS_PI = 2; // 0x2
     field public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13; // 0xd
     field public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12; // 0xc
-    field public static final int IDENTIFIER_TYPE_VENDOR1_PRIMARY = 14; // 0xe
-    field public static final int IDENTIFIER_TYPE_VENDOR2_PRIMARY = 15; // 0xf
-    field public static final int IDENTIFIER_TYPE_VENDOR3_PRIMARY = 16; // 0x10
-    field public static final int IDENTIFIER_TYPE_VENDOR4_PRIMARY = 17; // 0x11
+    field public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = 1999; // 0x7cf
+    field public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = 1000; // 0x3e8
     field public static final int PROGRAM_TYPE_AM = 1; // 0x1
     field public static final int PROGRAM_TYPE_AM_HD = 3; // 0x3
     field public static final int PROGRAM_TYPE_DAB = 5; // 0x5
@@ -17263,10 +17276,8 @@
     field public static final int PROGRAM_TYPE_FM = 2; // 0x2
     field public static final int PROGRAM_TYPE_FM_HD = 4; // 0x4
     field public static final int PROGRAM_TYPE_SXM = 7; // 0x7
-    field public static final int PROGRAM_TYPE_VENDOR1 = 8; // 0x8
-    field public static final int PROGRAM_TYPE_VENDOR2 = 9; // 0x9
-    field public static final int PROGRAM_TYPE_VENDOR3 = 10; // 0xa
-    field public static final int PROGRAM_TYPE_VENDOR4 = 11; // 0xb
+    field public static final int PROGRAM_TYPE_VENDOR_END = 1999; // 0x7cf
+    field public static final int PROGRAM_TYPE_VENDOR_START = 1000; // 0x3e8
   }
 
   public static final class ProgramSelector.Identifier implements android.os.Parcelable {
@@ -17394,7 +17405,7 @@
     method public java.lang.String getProduct();
     method public java.lang.String getSerial();
     method public java.lang.String getServiceName();
-    method public java.lang.String getVendorInfo();
+    method public java.util.Map<java.lang.String, java.lang.String> getVendorInfo();
     method public java.lang.String getVersion();
     method public boolean isBackgroundScanningSupported();
     method public boolean isCaptureSupported();
@@ -17411,7 +17422,7 @@
     method public android.hardware.radio.ProgramSelector getSelector();
     method public int getSignalStrength();
     method public deprecated int getSubChannel();
-    method public java.lang.String getVendorInfo();
+    method public java.util.Map<java.lang.String, java.lang.String> getVendorInfo();
     method public boolean isDigital();
     method public boolean isLive();
     method public boolean isMuted();
@@ -17475,7 +17486,7 @@
     method public abstract int getConfiguration(android.hardware.radio.RadioManager.BandConfig[]);
     method public abstract boolean getMute();
     method public abstract int getProgramInformation(android.hardware.radio.RadioManager.ProgramInfo[]);
-    method public abstract java.util.List<android.hardware.radio.RadioManager.ProgramInfo> getProgramList(java.lang.String);
+    method public abstract java.util.List<android.hardware.radio.RadioManager.ProgramInfo> getProgramList(java.util.Map<java.lang.String, java.lang.String>);
     method public abstract boolean hasControl();
     method public abstract boolean isAnalogForced();
     method public abstract boolean isAntennaConnected();
@@ -24688,6 +24699,7 @@
     method public android.graphics.Bitmap getFrameAtTime(long, int);
     method public android.graphics.Bitmap getFrameAtTime(long);
     method public android.graphics.Bitmap getFrameAtTime();
+    method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int);
     method public void release();
     method public void setDataSource(java.lang.String) throws java.lang.IllegalArgumentException;
     method public void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.lang.IllegalArgumentException;
@@ -40274,6 +40286,13 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.LuhnChecksumValidator> CREATOR;
   }
 
+  public final class RegexValidator implements android.os.Parcelable android.service.autofill.Validator {
+    ctor public RegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR;
+  }
+
   public final class SaveCallback {
     method public void onFailure(java.lang.CharSequence);
     method public void onSuccess();
@@ -40314,13 +40333,6 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
-  public final class SimpleRegexValidator implements android.os.Parcelable android.service.autofill.Validator {
-    ctor public SimpleRegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern);
-    method public int describeContents();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.SimpleRegexValidator> CREATOR;
-  }
-
   public abstract interface Transformation {
   }
 
@@ -44819,6 +44831,7 @@
     method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public java.lang.String getNameForUid(int);
+    method public java.lang.String[] getNamesForUids(int[]);
     method public int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public int[] getPackageGids(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -55732,7 +55745,9 @@
     method public android.graphics.Typeface getTypeface();
     method public android.text.style.URLSpan[] getUrls();
     method public boolean hasSelection();
+    method public boolean isAllCaps();
     method public boolean isCursorVisible();
+    method public boolean isElegantTextHeight();
     method public boolean isInputMethodTarget();
     method public boolean isSuggestionsEnabled();
     method public boolean isTextSelectable();
diff --git a/api/test-current.txt b/api/test-current.txt
index e1ba274..f14d51d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -10544,6 +10544,7 @@
     method public java.io.OutputStream openWrite(java.lang.String, long, long) throws java.io.IOException;
     method public void removeSplit(java.lang.String) throws java.io.IOException;
     method public void setStagingProgress(float);
+    method public void transfer(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
   }
 
   public static abstract class PackageInstaller.SessionCallback {
@@ -10561,10 +10562,16 @@
     method public android.graphics.Bitmap getAppIcon();
     method public java.lang.CharSequence getAppLabel();
     method public java.lang.String getAppPackageName();
+    method public int getInstallLocation();
     method public int getInstallReason();
     method public java.lang.String getInstallerPackageName();
+    method public int getMode();
+    method public int getOriginatingUid();
+    method public android.net.Uri getOriginatingUri();
     method public float getProgress();
+    method public android.net.Uri getReferrerUri();
     method public int getSessionId();
+    method public long getSize();
     method public boolean isActive();
     method public boolean isSealed();
     method public void writeToParcel(android.os.Parcel, int);
@@ -11946,7 +11953,7 @@
     method public boolean needUpgrade(int);
     method protected void onAllReferencesReleased();
     method public static android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int);
-    method public static android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.OpenParams);
+    method public static android.database.sqlite.SQLiteDatabase openDatabase(java.io.File, android.database.sqlite.SQLiteDatabase.OpenParams);
     method public static android.database.sqlite.SQLiteDatabase openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int, android.database.DatabaseErrorHandler);
     method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.io.File, android.database.sqlite.SQLiteDatabase.CursorFactory);
     method public static android.database.sqlite.SQLiteDatabase openOrCreateDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory);
@@ -13898,6 +13905,7 @@
   public class Typeface {
     method public static android.graphics.Typeface create(java.lang.String, int);
     method public static android.graphics.Typeface create(android.graphics.Typeface, int);
+    method public static android.graphics.Typeface create(android.graphics.Typeface, int, boolean);
     method public static android.graphics.Typeface createFromAsset(android.content.res.AssetManager, java.lang.String);
     method public static android.graphics.Typeface createFromFile(java.io.File);
     method public static android.graphics.Typeface createFromFile(java.lang.String);
@@ -15936,7 +15944,6 @@
     field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
     field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
     field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
-    field public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6; // 0x6
     field public static final int FINGERPRINT_ERROR_VENDOR = 8; // 0x8
   }
 
@@ -22878,6 +22885,7 @@
     method public android.graphics.Bitmap getFrameAtTime(long, int);
     method public android.graphics.Bitmap getFrameAtTime(long);
     method public android.graphics.Bitmap getFrameAtTime();
+    method public android.graphics.Bitmap getScaledFrameAtTime(long, int, int, int);
     method public void release();
     method public void setDataSource(java.lang.String) throws java.lang.IllegalArgumentException;
     method public void setDataSource(java.lang.String, java.util.Map<java.lang.String, java.lang.String>) throws java.lang.IllegalArgumentException;
@@ -37275,6 +37283,14 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.LuhnChecksumValidator> CREATOR;
   }
 
+  public final class RegexValidator implements android.os.Parcelable android.service.autofill.Validator {
+    ctor public RegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern);
+    method public int describeContents();
+    method public boolean isValid(android.service.autofill.ValueFinder);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.RegexValidator> CREATOR;
+  }
+
   public final class SaveCallback {
     method public void onFailure(java.lang.CharSequence);
     method public void onSuccess();
@@ -37315,14 +37331,6 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
-  public final class SimpleRegexValidator implements android.os.Parcelable android.service.autofill.Validator {
-    ctor public SimpleRegexValidator(android.view.autofill.AutofillId, java.util.regex.Pattern);
-    method public int describeContents();
-    method public boolean isValid(android.service.autofill.ValueFinder);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.SimpleRegexValidator> CREATOR;
-  }
-
   public abstract interface Transformation {
   }
 
@@ -41371,6 +41379,7 @@
     method public android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public java.lang.String getNameForUid(int);
+    method public java.lang.String[] getNamesForUids(int[]);
     method public int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public int[] getPackageGids(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public android.content.pm.PackageInfo getPackageInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -52106,7 +52115,9 @@
     method public android.graphics.Typeface getTypeface();
     method public android.text.style.URLSpan[] getUrls();
     method public boolean hasSelection();
+    method public boolean isAllCaps();
     method public boolean isCursorVisible();
+    method public boolean isElegantTextHeight();
     method public boolean isInputMethodTarget();
     method public boolean isSuggestionsEnabled();
     method public boolean isTextSelectable();
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index c6d83f5..b69ef1c 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -152,7 +152,7 @@
                 System.out.println(pretty);
             } else {
                 if (results != null) {
-                    for (String key : results.keySet()) {
+                    for (String key : sorted(results.keySet())) {
                         System.out.println(
                                 "INSTRUMENTATION_RESULT: " + key + "=" + results.get(key));
                     }
@@ -163,6 +163,10 @@
 
         @Override
         public void onError(String errorText, boolean commandError) {
+            if (mRawMode) {
+                System.out.println("onError: commandError=" + commandError + " message="
+                        + errorText);
+            }
             // The regular BaseCommand error printing will print the commandErrors.
             if (!commandError) {
                 System.out.println(errorText);
diff --git a/cmds/hid/jni/com_android_commands_hid_Device.cpp b/cmds/hid/jni/com_android_commands_hid_Device.cpp
index 107dc86..9d44212 100644
--- a/cmds/hid/jni/com_android_commands_hid_Device.cpp
+++ b/cmds/hid/jni/com_android_commands_hid_Device.cpp
@@ -27,8 +27,8 @@
 #include <unistd.h>
 
 #include <jni.h>
-#include <JNIHelp.h>
-#include <ScopedPrimitiveArray.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
 #include <ScopedUtfChars.h>
 #include <android/looper.h>
 #include <android/log.h>
diff --git a/cmds/incident_helper/Android.bp b/cmds/incident_helper/Android.bp
index 3ea823c..0532083 100644
--- a/cmds/incident_helper/Android.bp
+++ b/cmds/incident_helper/Android.bp
@@ -10,7 +10,7 @@
 
     srcs: [
         "IncidentHelper.cpp",
-        "strutil.cpp",
+        "ih_util.cpp",
     ],
 
     shared_libs: [
@@ -38,6 +38,7 @@
 
     srcs: [
         "tests/IncidentHelper_test.cpp",
+        "tests/ih_util_test.cpp",
     ],
 
     data: [
diff --git a/cmds/incident_helper/IncidentHelper.cpp b/cmds/incident_helper/IncidentHelper.cpp
index 89d8947..aa06595 100644
--- a/cmds/incident_helper/IncidentHelper.cpp
+++ b/cmds/incident_helper/IncidentHelper.cpp
@@ -17,14 +17,13 @@
 #define LOG_TAG "incident_helper"
 
 #include "IncidentHelper.h"
-#include "strutil.h"
+#include "ih_util.h"
 
 #include "frameworks/base/core/proto/android/os/kernelwake.pb.h"
+#include "frameworks/base/core/proto/android/os/procrank.pb.h"
 
-#include <algorithm>
 #include <android-base/file.h>
 #include <unistd.h>
-#include <sstream>
 #include <string>
 #include <vector>
 
@@ -67,42 +66,34 @@
 const string KERNEL_WAKEUP_LINE_DELIMITER = "\t";
 
 status_t KernelWakesParser::Parse(const int in, const int out) const {
-    // read the content, this is not memory-efficient though since it loads everything
-    // However the data will be held in proto anyway, and incident_helper is less critical
-    string content;
-    if (!ReadFdToString(in, &content)) {
-        fprintf(stderr, "[%s]Failed to read data from incidentd\n", this->name.string());
-        return -1;
-    }
-
-    istringstream iss(content);
+    Reader reader(in);
     string line;
-    vector<string> header;  // the header of /d/wakeup_sources
-    vector<string> record;  // retain each record
+    header_t header;  // the header of /d/wakeup_sources
+    record_t record;  // retain each record
     int nline = 0;
 
     KernelWakeSources proto;
 
     // parse line by line
-    while (getline(iss, line)) {
+    while (reader.readLine(line)) {
+        if (line.empty()) continue;
         // parse head line
-        if (nline == 0) {
-          split(line, &header);
-          if (!assertHeaders(kernel_wake_headers, header)) {
-            fprintf(stderr, "[%s]Bad header:\n%s\n", this->name.string(), line.c_str());
-            return BAD_VALUE;
-          }
-          nline++;
-          continue;
+        if (nline++ == 0) {
+            split(line, header, KERNEL_WAKEUP_LINE_DELIMITER);
+            if (!assertHeaders(kernel_wake_headers, header)) {
+                fprintf(stderr, "[%s]Bad header:\n%s\n", this->name.string(), line.c_str());
+                return BAD_VALUE;
+            }
+            continue;
         }
 
         // parse for each record, the line delimiter is \t only!
-        split(line, &record, KERNEL_WAKEUP_LINE_DELIMITER);
+        split(line, record, KERNEL_WAKEUP_LINE_DELIMITER);
 
         if (record.size() != header.size()) {
-          // TODO: log this to incident report!
-          fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, line.c_str());
-          continue;
+            // TODO: log this to incident report!
+            fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline, line.c_str());
+            continue;
         }
 
         WakeupSourceProto* source = proto.add_wakeup_sources();
@@ -118,17 +109,106 @@
         source->set_max_time(atol(record.at(7).c_str()));
         source->set_last_change(atol(record.at(8).c_str()));
         source->set_prevent_suspend_time(atol(record.at(9).c_str()));
-
-        nline++;
     }
 
-    fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize());
+    if (!reader.ok(line)) {
+        fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
+        return -1;
+    }
 
     if (!proto.SerializeToFileDescriptor(out)) {
         fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
         return -1;
     }
-    close(out);
-
+    fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize());
     return NO_ERROR;
 }
+
+// ================================================================================
+const char* procrank_headers[] = {
+    "PID",          // id:  1
+    "Vss",          // id:  2
+    "Rss",          // id:  3
+    "Pss",          // id:  4
+    "Uss",          // id:  5
+    "Swap",         // id:  6
+    "PSwap",        // id:  7
+    "USwap",        // id:  8
+    "ZSwap",        // id:  9
+    "cmdline",      // id: 10
+};
+
+status_t ProcrankParser::Parse(const int in, const int out) const {
+    Reader reader(in);
+    string line, content;
+    header_t header;  // the header of /d/wakeup_sources
+    record_t record;  // retain each record
+    int nline = 0;
+
+    Procrank proto;
+
+    // parse line by line
+    while (reader.readLine(line)) {
+        if (line.empty()) continue;
+
+        // parse head line
+        if (nline++ == 0) {
+            split(line, header);
+            if (!assertHeaders(procrank_headers, header)) {
+                fprintf(stderr, "[%s]Bad header:\n%s\n", this->name.string(), line.c_str());
+                return BAD_VALUE;
+            }
+            continue;
+        }
+
+        split(line, record);
+        if (record.size() != header.size()) {
+            if (record[record.size() - 1] == "TOTAL") { // TOTAL record
+                ProcessProto* total = proto.mutable_summary()->mutable_total();
+                total->set_pss(atol(record.at(0).substr(0, record.at(0).size() - 1).c_str()));
+                total->set_uss(atol(record.at(1).substr(0, record.at(1).size() - 1).c_str()));
+                total->set_swap(atol(record.at(2).substr(0, record.at(2).size() - 1).c_str()));
+                total->set_pswap(atol(record.at(3).substr(0, record.at(3).size() - 1).c_str()));
+                total->set_uswap(atol(record.at(4).substr(0, record.at(4).size() - 1).c_str()));
+                total->set_zswap(atol(record.at(5).substr(0, record.at(5).size() - 1).c_str()));
+            } else if (record[0] == "ZRAM:") {
+                split(line, record, ":");
+                proto.mutable_summary()->mutable_zram()->set_raw_text(record[1]);
+            } else if (record[0] == "RAM:") {
+                split(line, record, ":");
+                proto.mutable_summary()->mutable_ram()->set_raw_text(record[1]);
+            } else {
+                fprintf(stderr, "[%s]Line %d has missing fields\n%s\n", this->name.string(), nline,
+                    line.c_str());
+            }
+            continue;
+        }
+
+        ProcessProto* process = proto.add_processes();
+        // int32
+        process->set_pid(atoi(record.at(0).c_str()));
+        // int64, remove 'K' at the end
+        process->set_vss(atol(record.at(1).substr(0, record.at(1).size() - 1).c_str()));
+        process->set_rss(atol(record.at(2).substr(0, record.at(2).size() - 1).c_str()));
+        process->set_pss(atol(record.at(3).substr(0, record.at(3).size() - 1).c_str()));
+        process->set_uss(atol(record.at(4).substr(0, record.at(4).size() - 1).c_str()));
+        process->set_swap(atol(record.at(5).substr(0, record.at(5).size() - 1).c_str()));
+        process->set_pswap(atol(record.at(6).substr(0, record.at(6).size() - 1).c_str()));
+        process->set_uswap(atol(record.at(7).substr(0, record.at(7).size() - 1).c_str()));
+        process->set_zswap(atol(record.at(8).substr(0, record.at(8).size() - 1).c_str()));
+        // string
+        process->set_cmdline(record.at(9));
+    }
+
+    if (!reader.ok(line)) {
+        fprintf(stderr, "Bad read from fd %d: %s\n", in, line.c_str());
+        return -1;
+    }
+
+    if (!proto.SerializeToFileDescriptor(out)) {
+        fprintf(stderr, "[%s]Error writing proto back\n", this->name.string());
+        return -1;
+    }
+    fprintf(stderr, "[%s]Proto size: %d bytes\n", this->name.string(), proto.ByteSize());
+    return NO_ERROR;
+}
\ No newline at end of file
diff --git a/cmds/incident_helper/IncidentHelper.h b/cmds/incident_helper/IncidentHelper.h
index 40792a8..736f848 100644
--- a/cmds/incident_helper/IncidentHelper.h
+++ b/cmds/incident_helper/IncidentHelper.h
@@ -70,4 +70,17 @@
     virtual status_t Parse(const int in, const int out) const;
 };
 
+/**
+ * Procrank parser, parses text produced by command procrank
+ */
+extern const char* procrank_headers[];
+
+class ProcrankParser : public TextParserBase {
+public:
+    ProcrankParser() : TextParserBase(String8("ProcrankParser")) {};
+    ~ProcrankParser() {};
+
+    virtual status_t Parse(const int in, const int out) const;
+};
+
 #endif  // INCIDENT_HELPER_H
diff --git a/cmds/incident_helper/README.md b/cmds/incident_helper/README.md
index 967f1e3..866cc20 100644
--- a/cmds/incident_helper/README.md
+++ b/cmds/incident_helper/README.md
@@ -1,5 +1,7 @@
 # incident_helper
 
+It is an executable used to help parsing text format data to protobuf.
+
 ## How to build, deploy, unit test
 
 For the first time, build the test and create an empty directly on device:
@@ -15,3 +17,10 @@
 adb push $OUT/data/nativetest64/incident_helper_test/* /data/nativetest64/incident_helper_test/ && \
 adb shell /data/nativetest64/incident_helper_test/incident_helper_test 2>/dev/null
 ```
+## How to adapt proto changes
+
+If add a new proto file, add it in Android.bp under frameworks/base/ and make incident helper
+
+```
+root$ make -j48 incident_helper
+```
diff --git a/cmds/incident_helper/ih_util.cpp b/cmds/incident_helper/ih_util.cpp
new file mode 100644
index 0000000..bbb625f
--- /dev/null
+++ b/cmds/incident_helper/ih_util.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "incident_helper"
+
+#include "ih_util.h"
+
+#include <sstream>
+#include <unistd.h>
+
+const ssize_t BUFFER_SIZE = 16 * 1024; // 4KB
+
+std::string trim(const std::string& s, const std::string& whitespace) {
+    const auto head = s.find_first_not_of(whitespace);
+    if (head == std::string::npos) return "";
+
+    const auto tail = s.find_last_not_of(whitespace);
+    return s.substr(head, tail - head + 1);
+}
+
+// This is similiar to Split in android-base/file.h, but it won't add empty string
+void split(const std::string& line, std::vector<std::string>& words, const std::string& delimiters) {
+    words.clear();  // clear the buffer before split
+
+    size_t base = 0;
+    size_t found;
+    while (true) {
+        found = line.find_first_of(delimiters, base);
+        if (found != base) {
+            std::string word = trim(line.substr(base, found - base));
+            if (!word.empty()) {
+                words.push_back(word);
+            }
+        }
+        if (found == line.npos) break;
+        base = found + 1;
+    }
+}
+
+bool assertHeaders(const char* expected[], const std::vector<std::string>& actual) {
+    for (size_t i = 0; i < actual.size(); i++) {
+        if (expected[i] == NULL || std::string(expected[i]) != actual[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+Reader::Reader(const int fd) : Reader(fd, BUFFER_SIZE) {};
+
+Reader::Reader(const int fd, const size_t capacity)
+        : mFd(fd), mMaxSize(capacity), mBufSize(0), mRead(0), mFlushed(0)
+{
+    mBuf = capacity > 0 ? (char*)malloc(capacity * sizeof(char)) : NULL;
+    mStatus = mFd < 0 ? "Negative fd" : (capacity == 0 ? "Zero buffer capacity" : "");
+}
+
+Reader::~Reader()
+{
+    free(mBuf);
+}
+
+bool Reader::readLine(std::string& line, const char newline) {
+    if (!ok(line)) return false; // bad status
+    std::stringstream ss;
+    while (!EOR()) {
+        // read if available
+        if (mFd != -1 && mBufSize != mMaxSize) {
+            ssize_t amt = 0;
+            if (mRead >= mFlushed) {
+                amt = ::read(mFd, mBuf + mRead, mMaxSize - mRead);
+            } else {
+                amt = ::read(mFd, mBuf + mRead, mFlushed - mRead);
+            }
+            if (amt < 0) {
+                mStatus = "Fail to read from fd";
+                return false;
+            } else if (amt == 0) {
+                close(mFd);
+                mFd = -1;
+            }
+            mRead += amt;
+            mBufSize += amt;
+        }
+
+        bool meetsNewLine = false;
+        if (mBufSize > 0) {
+            int start = mFlushed;
+            int end = mFlushed < mRead ? mRead : mMaxSize;
+            while (mFlushed < end && mBuf[mFlushed++] != newline && mBufSize > 0) mBufSize--;
+            meetsNewLine = (mBuf[mFlushed-1] == newline);
+            if (meetsNewLine) mBufSize--; // deduct the new line character
+            size_t len = meetsNewLine ? mFlushed - start - 1 : mFlushed - start;
+            ss.write(mBuf + start, len);
+        }
+
+        if (mRead >= (int) mMaxSize) mRead = 0;
+        if (mFlushed >= (int) mMaxSize) mFlushed = 0;
+
+        if (EOR() || meetsNewLine) {
+            line.assign(ss.str());
+            return true;
+        }
+    }
+    return false;
+}
+
+bool Reader::ok(std::string& error) {
+    error.assign(mStatus);
+    return mStatus.empty();
+}
diff --git a/cmds/incident_helper/ih_util.h b/cmds/incident_helper/ih_util.h
new file mode 100644
index 0000000..9e0c18e
--- /dev/null
+++ b/cmds/incident_helper/ih_util.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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 INCIDENT_HELPER_UTIL_H
+#define INCIDENT_HELPER_UTIL_H
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+typedef std::vector<std::string> header_t;
+typedef std::vector<std::string> record_t;
+
+const char DEFAULT_NEWLINE = '\n';
+const std::string DEFAULT_WHITESPACE = " \t";
+
+std::string trim(const std::string& s, const std::string& whitespace = DEFAULT_WHITESPACE);
+
+void split(const std::string& line, std::vector<std::string>& words,
+    const std::string& delimiters = DEFAULT_WHITESPACE);
+
+bool assertHeaders(const char* expected[], const std::vector<std::string>& actual);
+
+/**
+ * Reader class reads data from given fd in streaming fashion.
+ * The buffer size is controlled by capacity parameter.
+ */
+class Reader
+{
+public:
+    Reader(const int fd);
+    Reader(const int fd, const size_t capacity);
+    ~Reader();
+
+    bool readLine(std::string& line, const char newline = DEFAULT_NEWLINE);
+    bool ok(std::string& error);
+
+private:
+    int mFd; // set mFd to -1 when read EOF()
+    const size_t mMaxSize;
+    size_t mBufSize;
+    char* mBuf; // implements a circular buffer
+
+    int mRead;
+    int mFlushed;
+    std::string mStatus;
+    // end of read
+    inline bool EOR() { return mFd == -1 && mBufSize == 0; };
+};
+
+#endif  // INCIDENT_HELPER_UTIL_H
diff --git a/cmds/incident_helper/main.cpp b/cmds/incident_helper/main.cpp
index 1e25c85..333344b 100644
--- a/cmds/incident_helper/main.cpp
+++ b/cmds/incident_helper/main.cpp
@@ -27,12 +27,11 @@
 using namespace std;
 
 static void usage(FILE* out) {
-    fprintf(out, "incident_helper is not designed to run manually, see README.md\n");
-    fprintf(out, "usage: incident_helper -s SECTION -i INPUT -o OUTPUT\n");
+    fprintf(out, "incident_helper is not designed to run manually,");
+    fprintf(out, "it reads from stdin and writes to stdout, see README.md for details.\n");
+    fprintf(out, "usage: incident_helper -s SECTION\n");
     fprintf(out, "REQUIRED:\n");
     fprintf(out, "  -s           section id, must be positive\n");
-    fprintf(out, "  -i           (default stdin) input fd\n");
-    fprintf(out, "  -o           (default stdout) output fd\n");
 }
 
 //=============================================================================
@@ -45,6 +44,8 @@
             return new ReverseParser();
 /* ========================================================================= */
         // IDs larger than 0 are reserved in incident.proto
+        case 2000:
+            return new ProcrankParser();
         case 2002:
             return new KernelWakesParser();
         default:
@@ -59,9 +60,7 @@
     // Parse the args
     int opt;
     int sectionID = 0;
-    int inputFd = STDIN_FILENO;
-    int outputFd = STDOUT_FILENO;
-    while ((opt = getopt(argc, argv, "hs:i:o:")) != -1) {
+    while ((opt = getopt(argc, argv, "hs:")) != -1) {
         switch (opt) {
             case 'h':
                 usage(stdout);
@@ -69,30 +68,14 @@
             case 's':
                 sectionID = atoi(optarg);
                 break;
-            case 'i':
-                inputFd = atoi(optarg);
-                break;
-            case 'o':
-                outputFd = atoi(optarg);
-                break;
         }
     }
 
-    // Check mandatory parameters:
-    if (inputFd < 0) {
-        fprintf(stderr, "invalid input fd: %d\n", inputFd);
-        return 1;
-    }
-    if (outputFd < 0) {
-        fprintf(stderr, "invalid output fd: %d\n", outputFd);
-        return 1;
-    }
-
     fprintf(stderr, "Pasring section %d...\n", sectionID);
     TextParserBase* parser = selectParser(sectionID);
     if (parser != NULL) {
         fprintf(stderr, "Running parser: %s\n", parser->name.string());
-        status_t err = parser->Parse(inputFd, outputFd);
+        status_t err = parser->Parse(STDIN_FILENO, STDOUT_FILENO);
         if (err != NO_ERROR) {
             fprintf(stderr, "Parse error in section %d: %s\n", sectionID, strerror(-err));
             return -1;
diff --git a/cmds/incident_helper/strutil.cpp b/cmds/incident_helper/strutil.cpp
deleted file mode 100644
index 21b04a1..0000000
--- a/cmds/incident_helper/strutil.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#define LOG_TAG "incident_helper"
-
-#include "strutil.h"
-
-#include <sstream>
-
-std::string trim(const std::string& s, const std::string& whitespace) {
-    const auto head = s.find_first_not_of(whitespace);
-    if (head == std::string::npos) return "";
-
-    const auto tail = s.find_last_not_of(whitespace);
-    return s.substr(head, tail - head + 1);
-}
-
-// This is similiar to Split in android-base/file.h, but it won't add empty string
-void split(const std::string& line, std::vector<std::string>* words, const std::string& delimiters) {
-    words->clear();  // clear the buffer before split
-
-    size_t base = 0;
-    size_t found;
-    while (true) {
-        found = line.find_first_of(delimiters, base);
-        if (found != base) { // ignore empty string
-            // one char before found
-            words->push_back(line.substr(base, found - base));
-        }
-        if (found == line.npos) break;
-        base = found + 1;
-    }
-}
-
-bool assertHeaders(const char* expected[], const std::vector<std::string>& actual) {
-    for (size_t i = 0; i < actual.size(); i++) {
-        if (expected[i] == NULL || std::string(expected[i]) != actual.at(i)) {
-            return false;
-        }
-    }
-    return true;
-}
diff --git a/cmds/incident_helper/strutil.h b/cmds/incident_helper/strutil.h
deleted file mode 100644
index fcc164d..0000000
--- a/cmds/incident_helper/strutil.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2017 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 STRUTIL_H
-#define STRUTIL_H
-
-#include <string>
-#include <vector>
-
-const std::string DEFAULT_WHITESPACE = " \t";
-
-std::string trim(const std::string& s, const std::string& whitespace = DEFAULT_WHITESPACE);
-void split(const std::string& line, std::vector<std::string>* words,
-    const std::string& delimiters = DEFAULT_WHITESPACE);
-bool assertHeaders(const char* expected[], const std::vector<std::string>& actual);
-
-#endif  // STRUTIL_H
diff --git a/cmds/incident_helper/testdata/procrank.txt b/cmds/incident_helper/testdata/procrank.txt
new file mode 100644
index 0000000..5d2d8d2
--- /dev/null
+++ b/cmds/incident_helper/testdata/procrank.txt
@@ -0,0 +1,8 @@
+  PID       Vss      Rss      Pss      Uss     Swap    PSwap    USwap    ZSwap  cmdline
+ 1119  2607640K  339564K  180278K  114216K    1584K      46K       0K      10K  system_server
+  649    11016K    1448K      98K      48K     472K     342K     212K      75K  /vendor/bin/qseecomd
+                           ------   ------   ------   ------   ------   ------  ------
+                          1201993K  935300K   88164K   31069K   27612K    6826K  TOTAL
+
+ZRAM: 6828K physical used for 31076K in swap (524284K total swap)
+ RAM: 3843972K total, 281424K free, 116764K buffers, 1777452K cached, 1136K shmem, 217916K slab
\ No newline at end of file
diff --git a/cmds/incident_helper/tests/IncidentHelper_test.cpp b/cmds/incident_helper/tests/IncidentHelper_test.cpp
index 9d5d9e4..ac3e84d 100644
--- a/cmds/incident_helper/tests/IncidentHelper_test.cpp
+++ b/cmds/incident_helper/tests/IncidentHelper_test.cpp
@@ -17,10 +17,12 @@
 #include "IncidentHelper.h"
 
 #include "frameworks/base/core/proto/android/os/kernelwake.pb.h"
+#include "frameworks/base/core/proto/android/os/procrank.pb.h"
 
 #include <android-base/file.h>
 #include <android-base/test_utils.h>
 #include <gmock/gmock.h>
+#include <google/protobuf/message.h>
 #include <gtest/gtest.h>
 #include <string.h>
 #include <fcntl.h>
@@ -37,10 +39,19 @@
 class IncidentHelperTest : public Test {
 public:
     virtual void SetUp() override {
+        ASSERT_TRUE(tf.fd != -1);
+    }
 
+    std::string getSerializedString(::google::protobuf::Message& message) {
+        std::string expectedStr;
+        message.SerializeToFileDescriptor(tf.fd);
+        ReadFileToString(tf.path, &expectedStr);
+        return expectedStr;
     }
 
 protected:
+    TemporaryFile tf;
+
     const std::string kTestPath = GetExecutableDirectory();
     const std::string kTestDataPath = kTestPath + "/testdata/";
 };
@@ -61,9 +72,6 @@
     const std::string testFile = kTestDataPath + "kernel_wakeups.txt";
     KernelWakesParser parser;
     KernelWakeSources expected;
-    std::string expectedStr;
-    TemporaryFile tf;
-    ASSERT_TRUE(tf.fd != -1);
 
     WakeupSourceProto* record1 = expected.add_wakeup_sources();
     record1->set_name("ipc000000ab_ATFWD-daemon");
@@ -89,15 +97,12 @@
     record2->set_last_change(2067286206l);
     record2->set_prevent_suspend_time(0l);
 
-    ASSERT_TRUE(expected.SerializeToFileDescriptor(tf.fd));
-    ASSERT_TRUE(ReadFileToString(tf.path, &expectedStr));
-
     int fd = open(testFile.c_str(), O_RDONLY, 0444);
     ASSERT_TRUE(fd != -1);
 
     CaptureStdout();
     ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
-    EXPECT_EQ(GetCapturedStdout(), expectedStr);
+    EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
     close(fd);
 }
 
@@ -115,3 +120,54 @@
     EXPECT_THAT(GetCapturedStderr(), StrEq("[KernelWakeSources]Bad header:\nTHIS IS BAD HEADER\n"));
     close(fd);
 }
+
+TEST_F(IncidentHelperTest, ProcrankParser) {
+    const std::string testFile = kTestDataPath + "procrank.txt";
+    ProcrankParser parser;
+    Procrank expected;
+
+    ProcessProto* process1 = expected.add_processes();
+    process1->set_pid(1119);
+    process1->set_vss(2607640);
+    process1->set_rss(339564);
+    process1->set_pss(180278);
+    process1->set_uss(114216);
+    process1->set_swap(1584);
+    process1->set_pswap(46);
+    process1->set_uswap(0);
+    process1->set_zswap(10);
+    process1->set_cmdline("system_server");
+
+    ProcessProto* process2 = expected.add_processes();
+    process2->set_pid(649);
+    process2->set_vss(11016);
+    process2->set_rss(1448);
+    process2->set_pss(98);
+    process2->set_uss(48);
+    process2->set_swap(472);
+    process2->set_pswap(342);
+    process2->set_uswap(212);
+    process2->set_zswap(75);
+    process2->set_cmdline("/vendor/bin/qseecomd");
+
+    ProcessProto* total = expected.mutable_summary()->mutable_total();
+    total->set_pss(1201993);
+    total->set_uss(935300);
+    total->set_swap(88164);
+    total->set_pswap(31069);
+    total->set_uswap(27612);
+    total->set_zswap(6826);
+
+    expected.mutable_summary()->mutable_zram()
+        ->set_raw_text("6828K physical used for 31076K in swap (524284K total swap)");
+    expected.mutable_summary()->mutable_ram()
+        ->set_raw_text("3843972K total, 281424K free, 116764K buffers, 1777452K cached, 1136K shmem, 217916K slab");
+
+    int fd = open(testFile.c_str(), O_RDONLY, 0444);
+    ASSERT_TRUE(fd != -1);
+
+    CaptureStdout();
+    ASSERT_EQ(NO_ERROR, parser.Parse(fd, STDOUT_FILENO));
+    EXPECT_EQ(GetCapturedStdout(), getSerializedString(expected));
+    close(fd);
+}
diff --git a/cmds/incident_helper/tests/ih_util_test.cpp b/cmds/incident_helper/tests/ih_util_test.cpp
new file mode 100644
index 0000000..5158e0a
--- /dev/null
+++ b/cmds/incident_helper/tests/ih_util_test.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2017 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 "ih_util.h"
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <string>
+
+using namespace android::base;
+using namespace std;
+using ::testing::StrEq;
+
+TEST(IhUtilTest, Trim) {
+    EXPECT_THAT(trim(" \t 100 00\toooh \t wqrw  "), StrEq("100 00\toooh \t wqrw"));
+    EXPECT_THAT(trim(" \t 100 00\toooh \t wqrw  ", " "), StrEq("\t 100 00\toooh \t wqrw"));
+}
+
+TEST(IhUtilTest, Split) {
+    vector<string> result, expected;
+    split(" \t \t\t ", result);
+    EXPECT_EQ(expected, result);
+
+    split(" \t 100 00\toooh \t wqrw", result);
+    expected = { "100", "00", "oooh", "wqrw" };
+    EXPECT_EQ(expected, result);
+
+    split(" \t 100 00\toooh \t wqrw", result, "\t");
+    expected = { "100 00", "oooh", "wqrw" };
+    EXPECT_EQ(expected, result);
+
+    split("123,456,78_9", result, ",");
+    expected = { "123", "456", "78_9" };
+    EXPECT_EQ(expected, result);
+}
+
+TEST(IhUtilTest, Reader) {
+    TemporaryFile tf;
+    ASSERT_NE(tf.fd, -1);
+    ASSERT_TRUE(WriteStringToFile("test string\nsecond\nooo\n", tf.path, false));
+
+    Reader r(tf.fd);
+    string line;
+    ASSERT_TRUE(r.readLine(line));
+    EXPECT_THAT(line, StrEq("test string"));
+    ASSERT_TRUE(r.readLine(line));
+    EXPECT_THAT(line, StrEq("second"));
+    ASSERT_TRUE(r.readLine(line));
+    EXPECT_THAT(line, StrEq("ooo"));
+    ASSERT_FALSE(r.readLine(line));
+    ASSERT_TRUE(r.ok(line));
+}
+
+TEST(IhUtilTest, ReaderSmallBufSize) {
+    TemporaryFile tf;
+    ASSERT_NE(tf.fd, -1);
+    ASSERT_TRUE(WriteStringToFile("test string\nsecond\nooiecccojreo", tf.path, false));
+
+    Reader r(tf.fd, 5);
+    string line;
+    ASSERT_TRUE(r.readLine(line));
+    EXPECT_THAT(line, StrEq("test string"));
+    ASSERT_TRUE(r.readLine(line));
+    EXPECT_THAT(line, StrEq("second"));
+    ASSERT_TRUE(r.readLine(line));
+    EXPECT_THAT(line, StrEq("ooiecccojreo"));
+    ASSERT_FALSE(r.readLine(line));
+    ASSERT_TRUE(r.ok(line));
+}
+
+TEST(IhUtilTest, ReaderEmpty) {
+    TemporaryFile tf;
+    ASSERT_NE(tf.fd, -1);
+    ASSERT_TRUE(WriteStringToFile("", tf.path, false));
+
+    Reader r(tf.fd);
+    string line;
+    ASSERT_TRUE(r.readLine(line));
+    EXPECT_THAT(line, StrEq(""));
+    ASSERT_FALSE(r.readLine(line));
+    ASSERT_TRUE(r.ok(line));
+}
+
+TEST(IhUtilTest, ReaderMultipleEmptyLines) {
+    TemporaryFile tf;
+    ASSERT_NE(tf.fd, -1);
+    ASSERT_TRUE(WriteStringToFile("\n\n", tf.path, false));
+
+    Reader r(tf.fd);
+    string line;
+    ASSERT_TRUE(r.readLine(line));
+    EXPECT_THAT(line, StrEq(""));
+    ASSERT_TRUE(r.readLine(line));
+    EXPECT_THAT(line, StrEq(""));
+    ASSERT_FALSE(r.readLine(line));
+    EXPECT_THAT(line, StrEq(""));
+    ASSERT_TRUE(r.ok(line));
+}
+
+TEST(IhUtilTest, ReaderFailedNegativeFd) {
+    Reader r(-123);
+    string line;
+    EXPECT_FALSE(r.readLine(line));
+    EXPECT_FALSE(r.ok(line));
+    EXPECT_THAT(line, StrEq("Negative fd"));
+}
+
+TEST(IhUtilTest, ReaderFailedZeroBufferSize) {
+    Reader r(23, 0);
+    string line;
+    EXPECT_FALSE(r.readLine(line));
+    EXPECT_FALSE(r.ok(line));
+    EXPECT_THAT(line, StrEq("Zero buffer capacity"));
+}
+
+TEST(IhUtilTest, ReaderFailedBadFd) {
+    Reader r(1231432);
+    string line;
+    EXPECT_FALSE(r.readLine(line));
+    EXPECT_FALSE(r.ok(line));
+    EXPECT_THAT(line, StrEq("Fail to read from fd"));
+}
diff --git a/cmds/incidentd/Android.mk b/cmds/incidentd/Android.mk
index a8a5483..537c910 100644
--- a/cmds/incidentd/Android.mk
+++ b/cmds/incidentd/Android.mk
@@ -79,7 +79,10 @@
     src/Reporter.cpp \
     src/Section.cpp \
     src/protobuf.cpp \
+    src/report_directory.cpp \
+    src/section_list.cpp \
     tests/FdBuffer_test.cpp \
+    tests/Reporter_test.cpp \
     tests/Section_test.cpp \
 
 LOCAL_STATIC_LIBRARIES := \
diff --git a/cmds/incidentd/incidentd.rc b/cmds/incidentd/incidentd.rc
index d11e3cf..250a7f3 100644
--- a/cmds/incidentd/incidentd.rc
+++ b/cmds/incidentd/incidentd.rc
@@ -12,5 +12,5 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-#service incidentd /system/bin/incidentd
+# service incidentd /system/bin/incidentd
 #    class main
diff --git a/cmds/incidentd/src/FdBuffer.h b/cmds/incidentd/src/FdBuffer.h
index 7888442..03a6d18 100644
--- a/cmds/incidentd/src/FdBuffer.h
+++ b/cmds/incidentd/src/FdBuffer.h
@@ -98,7 +98,7 @@
     bool close() { return !(::close(mFds[0]) || ::close(mFds[1])); }
     ~Fpipe() { close(); }
 
-    inline status_t init() { return pipe(mFds); }
+    inline bool init() { return pipe(mFds) != -1; }
     inline int readFd() const { return mFds[0]; }
     inline int writeFd() const { return mFds[1]; }
 
diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp
index 7c6789e..c4b54bb 100644
--- a/cmds/incidentd/src/IncidentService.cpp
+++ b/cmds/incidentd/src/IncidentService.cpp
@@ -153,7 +153,6 @@
             break;
         }
         reporter->batch.add(request);
-        reporter->args.merge(request->args);
     }
 
     // Take the report, which might take a while. More requests might queue
@@ -235,7 +234,7 @@
         return Status::fromExceptionCode(Status::EX_SECURITY,
                 "Only system uid can call systemRunning");
     }
-    
+
     // When system_server is up and running, schedule the dropbox task to run.
     mHandler->scheduleSendBacklogToDropbox();
 
diff --git a/cmds/incidentd/src/Reporter.cpp b/cmds/incidentd/src/Reporter.cpp
index ba157de6..ea73bcd 100644
--- a/cmds/incidentd/src/Reporter.cpp
+++ b/cmds/incidentd/src/Reporter.cpp
@@ -35,7 +35,7 @@
 /**
  * The directory where the incident reports are stored.
  */
-static const String8 INCIDENT_DIRECTORY("/data/incidents");
+static const char* INCIDENT_DIRECTORY = "/data/misc/incidents/";
 
 // ================================================================================
 static status_t write_all(int fd, uint8_t const* buf, size_t size)
@@ -68,6 +68,7 @@
 // ================================================================================
 ReportRequestSet::ReportRequestSet()
     :mRequests(),
+     mSections(),
      mWritableCount(0),
      mMainFd(-1)
 {
@@ -77,10 +78,12 @@
 {
 }
 
+// TODO: dedup on exact same args and fd, report the status back to listener!
 void
 ReportRequestSet::add(const sp<ReportRequest>& request)
 {
     mRequests.push_back(request);
+    mSections.merge(request->args);
     mWritableCount++;
 }
 
@@ -122,11 +125,16 @@
     return mWritableCount > 0 ? NO_ERROR : err;
 }
 
+bool
+ReportRequestSet::containsSection(int id) {
+    return mSections.containsSection(id);
+}
 
 // ================================================================================
-Reporter::Reporter()
-    :args(),
-     batch()
+Reporter::Reporter() : Reporter(INCIDENT_DIRECTORY) { isTest = false; };
+
+Reporter::Reporter(const char* directory)
+    :batch()
 {
     char buf[100];
 
@@ -134,10 +142,15 @@
     mMaxSize = 100 * 1024 * 1024;
     mMaxCount = 100;
 
+    // string ends up with '/' is a directory
+    String8 dir = String8(directory);
+    if (directory[dir.size() - 1] != '/') dir += "/";
+    mIncidentDirectory = dir.string();
+
     // There can't be two at the same time because it's on one thread.
     mStartTime = time(NULL);
-    strftime(buf, sizeof(buf), "/incident-%Y%m%d-%H%M%S", localtime(&mStartTime));
-    mFilename = INCIDENT_DIRECTORY + buf;
+    strftime(buf, sizeof(buf), "incident-%Y%m%d-%H%M%S", localtime(&mStartTime));
+    mFilename = mIncidentDirectory + buf;
 }
 
 Reporter::~Reporter()
@@ -161,7 +174,7 @@
     }
     if (needMainFd) {
         // Create the directory
-        err = create_directory(INCIDENT_DIRECTORY);
+        if (!isTest) err = create_directory(mIncidentDirectory);
         if (err != NO_ERROR) {
             goto done;
         }
@@ -169,7 +182,7 @@
         // If there are too many files in the directory (for whatever reason),
         // delete the oldest ones until it's under the limit. Doing this first
         // does mean that we can go over, so the max size is not a hard limit.
-        clean_directory(INCIDENT_DIRECTORY, mMaxSize, mMaxCount);
+        if (!isTest) clean_directory(mIncidentDirectory, mMaxSize, mMaxCount);
 
         // Open the file.
         err = create_file(&mainFd);
@@ -214,7 +227,7 @@
         const int id = (*section)->id;
         ALOGD("Taking incident report section %d '%s'", id, (*section)->name.string());
 
-        if (this->args.containsSection(id)) {
+        if (this->batch.containsSection(id)) {
             // Notify listener of starting
             for (ReportRequestSet::iterator it=batch.begin(); it!=batch.end(); it++) {
                 if ((*it)->listener != NULL && (*it)->args.containsSection(id)) {
@@ -270,7 +283,7 @@
         // If the status was ok, delete the file. If not, leave it around until the next
         // boot or the next checkin. If the directory gets too big older files will
         // be rotated out.
-        unlink(mFilename.c_str());
+        if(!isTest) unlink(mFilename.c_str());
     }
 
     return REPORT_FINISHED;
@@ -284,7 +297,7 @@
 {
     const char* filename = mFilename.c_str();
 
-    *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR, 0660);
+    *fd = open(filename, O_CREAT | O_TRUNC | O_RDWR | O_CLOEXEC, 0660);
     if (*fd < 0) {
         ALOGE("Couldn't open incident file: %s (%s)", filename, strerror(errno));
         return -errno;
@@ -303,20 +316,24 @@
     return NO_ERROR;
 }
 
-// ================================================================================
 Reporter::run_report_status_t
 Reporter::upload_backlog()
 {
     DIR* dir;
     struct dirent* entry;
     struct stat st;
+    status_t err;
 
-    if ((dir = opendir(INCIDENT_DIRECTORY.string())) == NULL) {
-        ALOGE("Couldn't open incident directory: %s", INCIDENT_DIRECTORY.string());
+    if ((err = create_directory(INCIDENT_DIRECTORY)) != NO_ERROR) {
+        ALOGE("directory doesn't exist: %s", strerror(-err));
+        return REPORT_FINISHED;
+    }
+
+    if ((dir = opendir(INCIDENT_DIRECTORY)) == NULL) {
+        ALOGE("Couldn't open incident directory: %s", INCIDENT_DIRECTORY);
         return REPORT_NEEDS_DROPBOX;
     }
 
-    String8 dirbase(INCIDENT_DIRECTORY + "/");
     sp<DropBoxManager> dropbox = new DropBoxManager();
 
     // Enumerate, count and add up size
@@ -324,7 +341,7 @@
         if (entry->d_name[0] == '.') {
             continue;
         }
-        String8 filename = dirbase + entry->d_name;
+        String8 filename = String8(INCIDENT_DIRECTORY) + entry->d_name;
         if (stat(filename.string(), &st) != 0) {
             ALOGE("Unable to stat file %s", filename.string());
             continue;
diff --git a/cmds/incidentd/src/Reporter.h b/cmds/incidentd/src/Reporter.h
index 5b86561..509611c 100644
--- a/cmds/incidentd/src/Reporter.h
+++ b/cmds/incidentd/src/Reporter.h
@@ -62,8 +62,10 @@
     iterator begin() { return mRequests.begin(); }
     iterator end() { return mRequests.end(); }
 
+    bool containsSection(int id);
 private:
     vector<sp<ReportRequest>> mRequests;
+    IncidentReportArgs mSections;
     int mWritableCount;
     int mMainFd;
 };
@@ -77,10 +79,10 @@
         REPORT_NEEDS_DROPBOX = 1
     };
 
-    IncidentReportArgs args;
     ReportRequestSet batch;
 
     Reporter();
+    Reporter(const char* directory);
     virtual ~Reporter();
 
     // Run the report as described in the batch and args parameters.
@@ -89,12 +91,16 @@
     static run_report_status_t upload_backlog();
 
 private:
+    String8 mIncidentDirectory;
+
     string mFilename;
     off_t mMaxSize;
     size_t mMaxCount;
     time_t mStartTime;
 
     status_t create_file(int* fd);
+
+    bool isTest = true; // default to true for testing
 };
 
 
diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp
index ddb54c1..9cc47ed 100644
--- a/cmds/incidentd/src/Section.cpp
+++ b/cmds/incidentd/src/Section.cpp
@@ -19,6 +19,7 @@
 #include "Section.h"
 #include "protobuf.h"
 
+#include <private/android_filesystem_config.h>
 #include <binder/IServiceManager.h>
 #include <mutex>
 #include <stdio.h>
@@ -29,15 +30,68 @@
 
 using namespace std;
 
-const int64_t REMOTE_CALL_TIMEOUT_MS = 10 * 1000; // 10 seconds
-const int64_t INCIDENT_HELPER_TIMEOUT_MS = 5 * 1000;  // 5 seconds
+const int   WAIT_MAX = 5;
+const struct timespec WAIT_INTERVAL_NS = {0, 200 * 1000 * 1000};
 const char* INCIDENT_HELPER = "/system/bin/incident_helper";
-const uid_t IH_UID = 9999;  // run incident_helper as nobody
-const gid_t IH_GID = 9999;
+
+static pid_t
+forkAndExecuteIncidentHelper(const int id, const char* name, Fpipe& p2cPipe, Fpipe& c2pPipe)
+{
+    const char* ihArgs[] { INCIDENT_HELPER, "-s", to_string(id).c_str(), NULL };
+
+    // fork used in multithreaded environment, avoid adding unnecessary code in child process
+    pid_t pid = fork();
+    if (pid == 0) {
+        // child process executes incident helper as nobody
+        if (setgid(AID_NOBODY) == -1) {
+            ALOGW("%s can't change gid: %s", name, strerror(errno));
+            _exit(EXIT_FAILURE);
+        }
+        if (setuid(AID_NOBODY) == -1) {
+            ALOGW("%s can't change uid: %s", name, strerror(errno));
+            _exit(EXIT_FAILURE);
+        }
+
+        if (dup2(p2cPipe.readFd(),  STDIN_FILENO)  != 0 || !p2cPipe.close() ||
+            dup2(c2pPipe.writeFd(), STDOUT_FILENO) != 1 || !c2pPipe.close()) {
+            ALOGW("%s can't setup stdin and stdout for incident helper", name);
+            _exit(EXIT_FAILURE);
+        }
+
+        execv(INCIDENT_HELPER, const_cast<char**>(ihArgs));
+
+        ALOGW("%s failed in incident helper process: %s", name, strerror(errno));
+        _exit(EXIT_FAILURE); // always exits with failure if any
+    }
+    // close the fds used in incident helper
+    close(p2cPipe.readFd());
+    close(c2pPipe.writeFd());
+    return pid;
+}
+
+static status_t killChild(pid_t pid) {
+    int status;
+    kill(pid, SIGKILL);
+    if (waitpid(pid, &status, 0) == -1) return -1;
+    return WIFEXITED(status) == 0 ? NO_ERROR : -WEXITSTATUS(status);
+}
+
+static status_t waitForChild(pid_t pid) {
+    int status;
+    bool died = false;
+    // wait for child to report status up to 1 seconds
+    for(int loop = 0; !died && loop < WAIT_MAX; loop++) {
+        if (waitpid(pid, &status, WNOHANG) == pid) died = true;
+        // sleep for 0.2 second
+        nanosleep(&WAIT_INTERVAL_NS, NULL);
+    }
+    if (!died) return killChild(pid);
+    return WIFEXITED(status) == 0 ? NO_ERROR : -WEXITSTATUS(status);
+}
 
 // ================================================================================
-Section::Section(int i)
-    :id(i)
+Section::Section(int i, const int64_t timeoutMs)
+    :id(i), timeoutMs(timeoutMs)
 {
 }
 
@@ -55,106 +109,57 @@
 }
 
 // ================================================================================
-FileSection::FileSection(int id, const char* filename)
-        : Section(id), mFilename(filename) {
-    name = "cat ";
-    name += filename;
+FileSection::FileSection(int id, const char* filename, const int64_t timeoutMs)
+        : Section(id, timeoutMs), mFilename(filename) {
+    name = filename;
 }
 
 FileSection::~FileSection() {}
 
 status_t FileSection::Execute(ReportRequestSet* requests) const {
-    Fpipe p2cPipe;
-    Fpipe c2pPipe;
-    FdBuffer buffer;
-
-    // initiate pipes to pass data to/from incident_helper
-    if (p2cPipe.init() == -1) {
-        return -errno;
-    }
-    if (c2pPipe.init() == -1) {
-        return -errno;
-    }
-
-    // fork a child process
-    pid_t pid = fork();
-
-    if (pid == -1) {
-        ALOGW("FileSection '%s' failed to fork", this->name.string());
-        return -errno;
-    }
-
-    // child process
-    if (pid == 0) {
-        if (setgid(IH_GID) == -1) {
-            ALOGW("FileSection '%s' can't change gid: %s", this->name.string(), strerror(errno));
-            exit(EXIT_FAILURE);
-        }
-        if (setuid(IH_UID) == -1) {
-            ALOGW("FileSection '%s' can't change uid: %s", this->name.string(), strerror(errno));
-            exit(EXIT_FAILURE);
-        }
-
-        if (dup2(p2cPipe.readFd(), STDIN_FILENO) != 0 || !p2cPipe.close()) {
-            ALOGW("FileSection '%s' failed to set up stdin: %s", this->name.string(), strerror(errno));
-            exit(EXIT_FAILURE);
-        }
-        if (dup2(c2pPipe.writeFd(), STDOUT_FILENO) != 1 || !c2pPipe.close()) {
-            ALOGW("FileSection '%s' failed to set up stdout: %s", this->name.string(), strerror(errno));
-            exit(EXIT_FAILURE);
-        }
-
-        // execute incident_helper to parse raw file data and generate protobuf
-        char sectionID[8];  // section id is expected to be smaller than 8 digits
-        sprintf(sectionID, "%d", this->id);
-        const char* args[]{INCIDENT_HELPER, "-s", sectionID, NULL};
-        execv(INCIDENT_HELPER, const_cast<char**>(args));
-
-        ALOGW("FileSection '%s' failed in child process: %s", this->name.string(), strerror(errno));
-        return -1;
-    }
-
-    // parent process
-
-    // close fds used in child process
-    close(p2cPipe.readFd());
-    close(c2pPipe.writeFd());
-
-    // read from mFilename and pump buffer to incident_helper
-    status_t err = NO_ERROR;
-    int fd = open(mFilename, O_RDONLY);
+    // read from mFilename first, make sure the file is available
+    // add O_CLOEXEC to make sure it is closed when exec incident helper
+    int fd = open(mFilename, O_RDONLY | O_CLOEXEC);
     if (fd == -1) {
        ALOGW("FileSection '%s' failed to open file", this->name.string());
        return -errno;
     }
 
-    err = buffer.readProcessedDataInStream(fd,
-        p2cPipe.writeFd(), c2pPipe.readFd(), INCIDENT_HELPER_TIMEOUT_MS);
-    if (err != NO_ERROR) {
-        ALOGW("FileSection '%s' failed to read data from incident helper: %s",
-            this->name.string(), strerror(-err));
-        kill(pid, SIGKILL); // kill child process if meets error
-        return err;
-    }
-
-    if (buffer.timedOut()) {
-        ALOGW("FileSection '%s' timed out reading from incident helper!", this->name.string());
-        kill(pid, SIGKILL); // kill the child process if timed out
-    }
-
-    // has to block here to reap child process
-    int status;
-    int w = waitpid(pid, &status, 0);
-    if (w < 0 || status == -1) {
-        ALOGW("FileSection '%s' abnormal child process: %s", this->name.string(), strerror(-err));
+    FdBuffer buffer;
+    Fpipe p2cPipe;
+    Fpipe c2pPipe;
+    // initiate pipes to pass data to/from incident_helper
+    if (!p2cPipe.init() || !c2pPipe.init()) {
+        ALOGW("FileSection '%s' failed to setup pipes", this->name.string());
         return -errno;
     }
 
-    // write parsed data to reporter
-    ALOGD("section '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(),
+    pid_t pid = forkAndExecuteIncidentHelper(this->id, this->name.string(), p2cPipe, c2pPipe);
+    if (pid == -1) {
+        ALOGW("FileSection '%s' failed to fork", this->name.string());
+        return -errno;
+    }
+
+    // parent process
+    status_t readStatus = buffer.readProcessedDataInStream(fd, p2cPipe.writeFd(), c2pPipe.readFd(),
+            this->timeoutMs);
+    if (readStatus != NO_ERROR || buffer.timedOut()) {
+        ALOGW("FileSection '%s' failed to read data from incident helper: %s, timedout: %s, kill: %s",
+            this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
+            strerror(-killChild(pid)));
+        return readStatus;
+    }
+
+    status_t ihStatus = waitForChild(pid);
+    if (ihStatus != NO_ERROR) {
+        ALOGW("FileSection '%s' abnormal child process: %s", this->name.string(), strerror(-ihStatus));
+        return ihStatus;
+    }
+
+    ALOGD("FileSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(),
             (int)buffer.durationMs());
     WriteHeader(requests, buffer.size());
-    err = buffer.write(requests);
+    status_t err = buffer.write(requests);
     if (err != NO_ERROR) {
         ALOGW("FileSection '%s' failed writing: %s", this->name.string(), strerror(-err));
         return err;
@@ -263,7 +268,7 @@
     pthread_attr_destroy(&attr);
 
     // Loop reading until either the timeout or the worker side is done (i.e. eof).
-    err = buffer.read(data->readFd(), REMOTE_CALL_TIMEOUT_MS);
+    err = buffer.read(data->readFd(), this->timeoutMs);
     if (err != NO_ERROR) {
         // TODO: Log this error into the incident report.
         ALOGW("WorkerThreadSection '%s' reader failed with error '%s'", this->name.string(),
@@ -309,7 +314,7 @@
     }
 
     // Write the data that was collected
-    ALOGD("section '%s' wrote %zd bytes in %d ms", name.string(), buffer.size(),
+    ALOGD("WorkerThreadSection '%s' wrote %zd bytes in %d ms", name.string(), buffer.size(),
             (int)buffer.durationMs());
     WriteHeader(requests, buffer.size());
     err = buffer.write(requests);
@@ -322,42 +327,116 @@
 }
 
 // ================================================================================
-CommandSection::CommandSection(int id, const char* first, ...)
-    :Section(id)
+void CommandSection::init(const char* command, va_list args)
+{
+    va_list copied_args;
+    va_copy(copied_args, args);
+    int numOfArgs = 0;
+    while(va_arg(args, const char*) != NULL) {
+        numOfArgs++;
+    }
+
+    // allocate extra 1 for command and 1 for NULL terminator
+    mCommand = (const char**)malloc(sizeof(const char*) * (numOfArgs + 2));
+
+    mCommand[0] = command;
+    name = command;
+    for (int i=0; i<numOfArgs; i++) {
+        const char* arg = va_arg(copied_args, const char*);
+        mCommand[i+1] = arg;
+        name += " ";
+        name += arg;
+    }
+    mCommand[numOfArgs+1] = NULL;
+    va_end(copied_args);
+}
+
+CommandSection::CommandSection(int id, const int64_t timeoutMs, const char* command, ...)
+        : Section(id, timeoutMs)
 {
     va_list args;
-    int count = 0;
-
-    va_start(args, first);
-    while (va_arg(args, const char*) != NULL) {
-        count++;
-    }
+    va_start(args, command);
+    init(command, args);
     va_end(args);
+}
 
-    mCommand = (const char**)malloc(sizeof(const char*) * count);
-
-    mCommand[0] = first;
-    name = first;
-    name += " ";
-    va_start(args, first);
-    for (int i=0; i<count; i++) {
-        const char* arg = va_arg(args, const char*);
-        mCommand[i+1] = arg;
-        if (arg != NULL) {
-            name += va_arg(args, const char*);
-            name += " ";
-        }
-    }
+CommandSection::CommandSection(int id, const char* command, ...)
+        : Section(id)
+{
+    va_list args;
+    va_start(args, command);
+    init(command, args);
     va_end(args);
 }
 
 CommandSection::~CommandSection()
 {
+    free(mCommand);
 }
 
 status_t
-CommandSection::Execute(ReportRequestSet* /*requests*/) const
+CommandSection::Execute(ReportRequestSet* requests) const
 {
+    FdBuffer buffer;
+    Fpipe cmdPipe;
+    Fpipe ihPipe;
+
+    if (!cmdPipe.init() || !ihPipe.init()) {
+        ALOGW("CommandSection '%s' failed to setup pipes", this->name.string());
+        return -errno;
+    }
+
+    pid_t cmdPid = fork();
+    if (cmdPid == -1) {
+        ALOGW("CommandSection '%s' failed to fork", this->name.string());
+        return -errno;
+    }
+    // child process to execute the command as root
+    if (cmdPid == 0) {
+        // replace command's stdout with ihPipe's write Fd
+        if (dup2(cmdPipe.writeFd(), STDOUT_FILENO) != 1 || !ihPipe.close() || !cmdPipe.close()) {
+            ALOGW("CommandSection '%s' failed to set up stdout: %s", this->name.string(), strerror(errno));
+            _exit(EXIT_FAILURE);
+        }
+        execv(this->mCommand[0], (char *const *) this->mCommand);
+        int err = errno; // record command error code
+        ALOGW("CommandSection '%s' failed in executing command: %s", this->name.string(), strerror(errno));
+        _exit(err); // exit with command error code
+    }
+    pid_t ihPid = forkAndExecuteIncidentHelper(this->id, this->name.string(), cmdPipe, ihPipe);
+    if (ihPid == -1) {
+        ALOGW("CommandSection '%s' failed to fork", this->name.string());
+        return -errno;
+    }
+
+    close(cmdPipe.writeFd());
+    status_t readStatus = buffer.read(ihPipe.readFd(), this->timeoutMs);
+    if (readStatus != NO_ERROR || buffer.timedOut()) {
+        ALOGW("CommandSection '%s' failed to read data from incident helper: %s, "
+            "timedout: %s, kill command: %s, kill incident helper: %s",
+            this->name.string(), strerror(-readStatus), buffer.timedOut() ? "true" : "false",
+            strerror(-killChild(cmdPid)), strerror(-killChild(ihPid)));
+        return readStatus;
+    }
+
+    // TODO: wait for command here has one trade-off: the failed status of command won't be detected until
+    //       buffer timeout, but it has advatage on starting the data stream earlier.
+    status_t cmdStatus = waitForChild(cmdPid);
+    status_t ihStatus  = waitForChild(ihPid);
+    if (cmdStatus != NO_ERROR || ihStatus != NO_ERROR) {
+        ALOGW("CommandSection '%s' abnormal child processes, return status: command: %s, incident helper: %s",
+            this->name.string(), strerror(-cmdStatus), strerror(-ihStatus));
+        return cmdStatus != NO_ERROR ? cmdStatus : ihStatus;
+    }
+
+    ALOGD("CommandSection '%s' wrote %zd bytes in %d ms", this->name.string(), buffer.size(),
+            (int)buffer.durationMs());
+    WriteHeader(requests, buffer.size());
+    status_t err = buffer.write(requests);
+    if (err != NO_ERROR) {
+        ALOGW("CommandSection '%s' failed writing: %s", this->name.string(), strerror(-err));
+        return err;
+    }
     return NO_ERROR;
 }
 
diff --git a/cmds/incidentd/src/Section.h b/cmds/incidentd/src/Section.h
index 35740e9..93b4848 100644
--- a/cmds/incidentd/src/Section.h
+++ b/cmds/incidentd/src/Section.h
@@ -19,22 +19,26 @@
 
 #include "FdBuffer.h"
 
+#include <stdarg.h>
 #include <utils/String8.h>
 #include <utils/String16.h>
 #include <utils/Vector.h>
 
 using namespace android;
 
+const int64_t REMOTE_CALL_TIMEOUT_MS = 10 * 1000; // 10 seconds
+
 /**
  * Base class for sections
  */
 class Section
 {
 public:
-    int id;
+    const int id;
+    const int64_t timeoutMs; // each section must have a timeout
     String8 name;
 
-    Section(int id);
+    Section(int id, const int64_t timeoutMs = REMOTE_CALL_TIMEOUT_MS);
     virtual ~Section();
 
     virtual status_t Execute(ReportRequestSet* requests) const = 0;
@@ -48,7 +52,7 @@
 class FileSection : public Section
 {
 public:
-    FileSection(int id, const char* filename);
+    FileSection(int id, const char* filename, const int64_t timeoutMs = 5000 /* 5 seconds */);
     virtual ~FileSection();
 
     virtual status_t Execute(ReportRequestSet* requests) const;
@@ -77,13 +81,18 @@
 class CommandSection : public Section
 {
 public:
-    CommandSection(int id, const char* first, ...);
+    CommandSection(int id, const int64_t timeoutMs, const char* command, ...);
+
+    CommandSection(int id, const char* command, ...);
+
     virtual ~CommandSection();
 
     virtual status_t Execute(ReportRequestSet* requests) const;
 
 private:
     const char** mCommand;
+
+    void init(const char* command, va_list args);
 };
 
 /**
diff --git a/cmds/incidentd/src/report_directory.cpp b/cmds/incidentd/src/report_directory.cpp
index f60b8ac..110902c 100644
--- a/cmds/incidentd/src/report_directory.cpp
+++ b/cmds/incidentd/src/report_directory.cpp
@@ -129,7 +129,8 @@
         return;
     }
 
-    String8 dirbase(String8(directory) + "/");
+    String8 dirbase(directory);
+    if (directory[dirbase.size() - 1] != '/') dirbase += "/";
 
     off_t totalSize = 0;
     size_t totalCount = 0;
diff --git a/cmds/incidentd/src/section_list.cpp b/cmds/incidentd/src/section_list.cpp
index 72c54d2..80ddb86 100644
--- a/cmds/incidentd/src/section_list.cpp
+++ b/cmds/incidentd/src/section_list.cpp
@@ -21,6 +21,7 @@
  */
 const Section* SECTION_LIST[] = {
     // Linux Services
+    new CommandSection(2000, "/system/xbin/procrank", NULL),
     new FileSection(2002, "/d/wakeup_sources"),
 
     // System Services
diff --git a/cmds/incidentd/tests/FdBuffer_test.cpp b/cmds/incidentd/tests/FdBuffer_test.cpp
index ca5eec8..ba8b77a 100644
--- a/cmds/incidentd/tests/FdBuffer_test.cpp
+++ b/cmds/incidentd/tests/FdBuffer_test.cpp
@@ -25,6 +25,7 @@
 
 const int READ_TIMEOUT = 5 * 1000;
 const int BUFFER_SIZE = 16 * 1024;
+const int QUICK_TIMEOUT_MS = 100;
 const std::string HEAD = "[OK]";
 
 using namespace android;
@@ -101,11 +102,11 @@
             write(c2pPipe.writeFd(), "poo", 3);
             sleep(1);
         }
-        exit(EXIT_FAILURE);
+        _exit(EXIT_FAILURE);
     } else {
         close(c2pPipe.writeFd());
 
-        status_t status = buffer.read(c2pPipe.readFd(), 500);
+        status_t status = buffer.read(c2pPipe.readFd(), QUICK_TIMEOUT_MS);
         ASSERT_EQ(NO_ERROR, status);
         EXPECT_TRUE(buffer.timedOut());
 
@@ -129,7 +130,7 @@
         close(p2cPipe.readFd());
         close(c2pPipe.writeFd());
         // Must exit here otherwise the child process will continue executing the test binary.
-        exit(EXIT_SUCCESS);
+        _exit(EXIT_SUCCESS);
     } else {
         close(p2cPipe.readFd());
         close(c2pPipe.writeFd());
@@ -161,7 +162,7 @@
         close(p2cPipe.readFd());
         close(c2pPipe.writeFd());
         // Must exit here otherwise the child process will continue executing the test binary.
-        exit(EXIT_SUCCESS);
+        _exit(EXIT_SUCCESS);
     } else {
         close(p2cPipe.readFd());
         close(c2pPipe.writeFd());
@@ -186,7 +187,7 @@
         ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd()));
         close(p2cPipe.readFd());
         close(c2pPipe.writeFd());
-        exit(EXIT_SUCCESS);
+        _exit(EXIT_SUCCESS);
     } else {
         close(p2cPipe.readFd());
         close(c2pPipe.writeFd());
@@ -212,7 +213,7 @@
         ASSERT_TRUE(DoDataStream(p2cPipe.readFd(), c2pPipe.writeFd()));
         close(p2cPipe.readFd());
         close(c2pPipe.writeFd());
-        exit(EXIT_SUCCESS);
+        _exit(EXIT_SUCCESS);
     } else {
         close(p2cPipe.readFd());
         close(c2pPipe.writeFd());
@@ -239,13 +240,13 @@
         while (true) {
             sleep(1);
         }
-        exit(EXIT_FAILURE);
+        _exit(EXIT_FAILURE);
     } else {
         close(p2cPipe.readFd());
         close(c2pPipe.writeFd());
 
         ASSERT_EQ(NO_ERROR, buffer.readProcessedDataInStream(tf.fd,
-            p2cPipe.writeFd(), c2pPipe.readFd(), 100));
+            p2cPipe.writeFd(), c2pPipe.readFd(), QUICK_TIMEOUT_MS));
         EXPECT_TRUE(buffer.timedOut());
         kill(pid, SIGKILL); // reap the child process
     }
diff --git a/cmds/incidentd/tests/Reporter_test.cpp b/cmds/incidentd/tests/Reporter_test.cpp
new file mode 100644
index 0000000..a774741
--- /dev/null
+++ b/cmds/incidentd/tests/Reporter_test.cpp
@@ -0,0 +1,197 @@
+// Copyright (C) 2017 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.
+
+#define LOG_TAG "incidentd"
+
+#include "Reporter.h"
+
+#include <android/os/BnIncidentReportStatusListener.h>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <dirent.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <string.h>
+
+
+using namespace android;
+using namespace android::base;
+using namespace android::binder;
+using namespace std;
+using ::testing::StrEq;
+using ::testing::Test;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStdout;
+
+class TestListener : public IIncidentReportStatusListener
+{
+public:
+    int startInvoked;
+    int finishInvoked;
+    int failedInvoked;
+    map<int, int> startSections;
+    map<int, int> finishSections;
+
+    TestListener() : startInvoked(0), finishInvoked(0), failedInvoked(0) {};
+    virtual ~TestListener() {};
+
+    virtual Status onReportStarted() {
+        startInvoked++;
+        return Status::ok();
+    };
+    virtual Status onReportSectionStatus(int section, int status) {
+        switch (status) {
+        case IIncidentReportStatusListener::STATUS_STARTING:
+            if (startSections.count(section) == 0)
+                startSections[section] = 0;
+            startSections[section] = startSections[section] + 1;
+            break;
+        case IIncidentReportStatusListener::STATUS_FINISHED:
+            if (finishSections.count(section) == 0)
+                finishSections[section] = 0;
+            finishSections[section] = finishSections[section] + 1;
+            break;
+        }
+        return Status::ok();
+    };
+    virtual Status onReportFinished() {
+        finishInvoked++;
+        return Status::ok();
+    };
+    virtual Status onReportFailed() {
+        failedInvoked++;
+        return Status::ok();
+    };
+
+protected:
+    IBinder* onAsBinder() override { return nullptr; };
+
+};
+
+class ReporterTest : public Test {
+public:
+    virtual void SetUp() {
+        reporter = new Reporter(td.path);
+        l = new TestListener();
+    }
+
+    vector<string> InspectFiles() {
+        DIR* dir;
+        struct dirent* entry;
+        vector<string> results;
+
+        string dirbase = string(td.path) + "/";
+        dir = opendir(td.path);
+
+        while ((entry = readdir(dir)) != NULL) {
+            if (entry->d_name[0] == '.') {
+                continue;
+            }
+            string filename = dirbase + entry->d_name;
+            string content;
+            ReadFileToString(filename, &content);
+            results.push_back(content);
+        }
+        return results;
+    }
+
+protected:
+    TemporaryDir td;
+    ReportRequestSet requests;
+    sp<Reporter> reporter;
+    sp<TestListener> l;
+};
+
+TEST_F(ReporterTest, IncidentReportArgs) {
+    IncidentReportArgs args1, args2;
+    args1.addSection(1);
+    args2.addSection(3);
+
+    args1.merge(args2);
+    ASSERT_TRUE(args1.containsSection(1));
+    ASSERT_FALSE(args1.containsSection(2));
+    ASSERT_TRUE(args1.containsSection(3));
+}
+
+TEST_F(ReporterTest, ReportRequestSetEmpty) {
+    requests.setMainFd(STDOUT_FILENO);
+
+    CaptureStdout();
+    requests.write((uint8_t *) "abcdef", 6);
+    EXPECT_THAT(GetCapturedStdout(), StrEq("abcdef"));
+}
+
+TEST_F(ReporterTest, WriteToStreamFdAndMainFd) {
+    TemporaryFile tf;
+    IncidentReportArgs args;
+    sp<ReportRequest> r = new ReportRequest(args, l, tf.fd);
+
+    requests.add(r);
+    requests.setMainFd(STDOUT_FILENO);
+
+    const char* data = "abcdef";
+
+    CaptureStdout();
+    requests.write((uint8_t *) data, 6);
+    EXPECT_THAT(GetCapturedStdout(), StrEq(data));
+
+    string content;
+    ASSERT_TRUE(ReadFileToString(tf.path, &content));
+    EXPECT_THAT(content, StrEq(data));
+}
+
+TEST_F(ReporterTest, RunReportEmpty) {
+    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
+    EXPECT_EQ(l->startInvoked, 0);
+    EXPECT_EQ(l->finishInvoked, 0);
+    EXPECT_TRUE(l->startSections.empty());
+    EXPECT_TRUE(l->finishSections.empty());
+    EXPECT_EQ(l->failedInvoked, 0);
+}
+
+TEST_F(ReporterTest, RunReportWithHeaders) {
+    IncidentReportArgs args1, args2;
+    args1.addSection(1);
+    args2.addSection(2);
+    std::vector<int8_t> header {'a', 'b', 'c', 'd', 'e'};
+    args2.addHeader(header);
+    sp<ReportRequest> r1 = new ReportRequest(args1, l, STDOUT_FILENO);
+    sp<ReportRequest> r2 = new ReportRequest(args2, l, STDOUT_FILENO);
+
+    reporter->batch.add(r1);
+    reporter->batch.add(r2);
+
+    CaptureStdout();
+    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
+    EXPECT_THAT(GetCapturedStdout(), StrEq("\n\x5" "abcde"));
+    EXPECT_EQ(l->startInvoked, 2);
+    EXPECT_EQ(l->finishInvoked, 2);
+    EXPECT_TRUE(l->startSections.empty());
+    EXPECT_TRUE(l->finishSections.empty());
+    EXPECT_EQ(l->failedInvoked, 0);
+}
+
+TEST_F(ReporterTest, RunReportToGivenDirectory) {
+    IncidentReportArgs args;
+    args.addHeader({'1', '2', '3'});
+    args.addHeader({'a', 'b', 'c', 'd'});
+    sp<ReportRequest> r = new ReportRequest(args, l, -1);
+    reporter->batch.add(r);
+
+    ASSERT_EQ(Reporter::REPORT_FINISHED, reporter->runReport());
+    vector<string> results = InspectFiles();
+    ASSERT_EQ((int)results.size(), 1);
+    EXPECT_EQ(results[0], "\n\x3" "123\n\x4" "abcd");
+}
diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp
index 18322b6..93771ff 100644
--- a/cmds/incidentd/tests/Section_test.cpp
+++ b/cmds/incidentd/tests/Section_test.cpp
@@ -22,6 +22,8 @@
 #include <gtest/gtest.h>
 #include <string.h>
 
+const int QUICK_TIMEOUT_MS = 100;
+
 using namespace android::base;
 using namespace std;
 using ::testing::StrEq;
@@ -62,7 +64,56 @@
 TEST(SectionTest, FileSectionTimeout) {
     TemporaryFile tf;
     // id -1 is timeout parser
-    FileSection fs(-1, tf.path);
+    FileSection fs(-1, tf.path, QUICK_TIMEOUT_MS);
     ReportRequestSet requests;
     ASSERT_EQ(NO_ERROR, fs.Execute(&requests));
+}
+
+TEST(SectionTest, CommandSectionConstructor) {
+    CommandSection cs1(1, "echo", "\"this is a test\"", "ooo", NULL);
+    CommandSection cs2(2, "single_command", NULL);
+    CommandSection cs3(1, 3123, "echo", "\"this is a test\"", "ooo", NULL);
+    CommandSection cs4(2, 43214, "single_command", NULL);
+
+    EXPECT_THAT(cs1.name.string(), StrEq("echo \"this is a test\" ooo"));
+    EXPECT_THAT(cs2.name.string(), StrEq("single_command"));
+    EXPECT_EQ(3123, cs3.timeoutMs);
+    EXPECT_EQ(43214, cs4.timeoutMs);
+    EXPECT_THAT(cs3.name.string(), StrEq("echo \"this is a test\" ooo"));
+    EXPECT_THAT(cs4.name.string(), StrEq("single_command"));
+}
+
+TEST(SectionTest, CommandSectionEcho) {
+    CommandSection cs(0, "/system/bin/echo", "about", NULL);
+    ReportRequestSet requests;
+    requests.setMainFd(STDOUT_FILENO);
+    CaptureStdout();
+    ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
+    EXPECT_THAT(GetCapturedStdout(), StrEq("\x02\x06\ntuoba"));
+}
+
+TEST(SectionTest, CommandSectionCommandTimeout) {
+    CommandSection cs(0, QUICK_TIMEOUT_MS, "/system/bin/yes", NULL);
+    ReportRequestSet requests;
+    ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
+}
+
+TEST(SectionTest, CommandSectionIncidentHelperTimeout) {
+    CommandSection cs(-1, QUICK_TIMEOUT_MS, "/system/bin/echo", "about", NULL);
+    ReportRequestSet requests;
+    requests.setMainFd(STDOUT_FILENO);
+    ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
+}
+
+TEST(SectionTest, CommandSectionBadCommand) {
+    CommandSection cs(0, "echo", "about", NULL);
+    ReportRequestSet requests;
+    ASSERT_EQ(NAME_NOT_FOUND, cs.Execute(&requests));
+}
+
+TEST(SectionTest, CommandSectionBadCommandAndTimeout) {
+    CommandSection cs(-1, QUICK_TIMEOUT_MS, "nonexistcommand", "-opt", NULL);
+    ReportRequestSet requests;
+    // timeout will return first
+    ASSERT_EQ(NO_ERROR, cs.Execute(&requests));
 }
\ No newline at end of file
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index af0a204..cec5db9 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -612,7 +612,7 @@
 
     /**
      * Get the controller for fingerprint gestures. This feature requires {@link
-     * AccessibilityServiceInfo#CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES}.
+     * AccessibilityServiceInfo#CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES}.
      *
      *<strong>Note: </strong> The service must be connected before this method is called.
      *
diff --git a/core/java/android/accessibilityservice/FingerprintGestureController.java b/core/java/android/accessibilityservice/FingerprintGestureController.java
index 9f04cea..c30030d 100644
--- a/core/java/android/accessibilityservice/FingerprintGestureController.java
+++ b/core/java/android/accessibilityservice/FingerprintGestureController.java
@@ -29,11 +29,11 @@
  * sensor, as long as the device has a sensor capable of detecting gestures.
  * <p>
  * This capability must be declared by the service as
- * {@link AccessibilityServiceInfo#CAPABILITY_CAN_CAPTURE_FINGERPRINT_GESTURES}. It also requires
+ * {@link AccessibilityServiceInfo#CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES}. It also requires
  * the permission {@link android.Manifest.permission#USE_FINGERPRINT}.
  * <p>
  * Because capturing fingerprint gestures may have side effects, services with the capability only
- * capture gestures when {@link AccessibilityServiceInfo#FLAG_CAPTURE_FINGERPRINT_GESTURES} is set.
+ * capture gestures when {@link AccessibilityServiceInfo#FLAG_REQUEST_FINGERPRINT_GESTURES} is set.
  * <p>
  * <strong>Note: </strong>The fingerprint sensor is used for authentication in critical use cases,
  * so services must carefully design their user's experience when performing gestures on the sensor.
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index a209d28..dd6ad55 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -395,7 +395,7 @@
 
     /**
      * Key to set default visibility for applications which don't satisfy conditions in
-     * {@link PACKAGE_NAME_KEY_LEGACY_VISIBLE}. If the value was not set by authenticator
+     * {@link #PACKAGE_NAME_KEY_LEGACY_VISIBLE}. If the value was not set by authenticator
      * {@link #VISIBILITY_USER_MANAGED_NOT_VISIBLE} is used.
      */
     public static final String PACKAGE_NAME_KEY_LEGACY_NOT_VISIBLE =
@@ -616,7 +616,7 @@
      * not authorized to view all accounts. This method can only be called by system apps and
      * authenticators managing the type.
      * Beginning API level {@link android.os.Build.VERSION_CODES#O} it also return accounts
-     * which user can make visible to the application (see {@link VISIBILITY_USER_MANAGED_VISIBLE}).
+     * which user can make visible to the application (see {@link #VISIBILITY_USER_MANAGED_VISIBLE}).
      *
      * @param type The type of accounts to return, null to retrieve all accounts
      * @param packageName The package name of the app for which the accounts are to be returned
@@ -654,7 +654,7 @@
      * of accounts made visible to it by user
      * (see {@link #newChooseAccountIntent(Account, List, String[], String,
      * String, String[], Bundle)}) or AbstractAcccountAuthenticator
-     * using {@link setAccountVisibility}.
+     * using {@link #setAccountVisibility}.
      * {@link android.Manifest.permission#GET_ACCOUNTS} permission is not used.
      *
      * <p>
@@ -800,7 +800,7 @@
      * of accounts made visible to it by user
      * (see {@link #newChooseAccountIntent(Account, List, String[], String,
      * String, String[], Bundle)}) or AbstractAcccountAuthenticator
-     * using {@link setAccountVisibility}.
+     * using {@link #setAccountVisibility}.
      * {@link android.Manifest.permission#GET_ACCOUNTS} permission is not used.
      *
      * <p>
@@ -2716,7 +2716,7 @@
      * On success the activity returns a Bundle with the account name and type specified using
      * keys {@link #KEY_ACCOUNT_NAME} and {@link #KEY_ACCOUNT_TYPE}.
      * Chosen account is marked as {@link #VISIBILITY_USER_MANAGED_VISIBLE} to the caller
-     * (see {@link setAccountVisibility}) and will be returned to it in consequent
+     * (see {@link #setAccountVisibility}) and will be returned to it in consequent
      * {@link #getAccountsByType}) calls.
      * <p>
      * The most common case is to call this with one account type, e.g.:
@@ -2771,7 +2771,7 @@
      * On success the activity returns a Bundle with the account name and type specified using
      * keys {@link #KEY_ACCOUNT_NAME} and {@link #KEY_ACCOUNT_TYPE}.
      * Chosen account is marked as {@link #VISIBILITY_USER_MANAGED_VISIBLE} to the caller
-     * (see {@link setAccountVisibility}) and will be returned to it in consequent
+     * (see {@link #setAccountVisibility}) and will be returned to it in consequent
      * {@link #getAccountsByType}) calls.
      * <p>
      * The most common case is to call this with one account type, e.g.:
diff --git a/core/java/android/annotation/TargetApi.java b/core/java/android/annotation/TargetApi.java
index ea17890..975318e 100644
--- a/core/java/android/annotation/TargetApi.java
+++ b/core/java/android/annotation/TargetApi.java
@@ -16,6 +16,7 @@
 package android.annotation;
 
 import static java.lang.annotation.ElementType.CONSTRUCTOR;
+import static java.lang.annotation.ElementType.FIELD;
 import static java.lang.annotation.ElementType.METHOD;
 import static java.lang.annotation.ElementType.TYPE;
 
@@ -25,7 +26,7 @@
 
 /** Indicates that Lint should treat this type as targeting a given API level, no matter what the
     project target is. */
-@Target({TYPE, METHOD, CONSTRUCTOR})
+@Target({TYPE, METHOD, CONSTRUCTOR, FIELD})
 @Retention(RetentionPolicy.CLASS)
 public @interface TargetApi {
     /**
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d432544..a0082e4 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2110,7 +2110,7 @@
      * is no longer available to third party
      * applications: the introduction of document-centric recents means
      * it can leak person information to the caller.  For backwards compatibility,
-     * it will still retu rn a small subset of its data: at least the caller's
+     * it will still return a small subset of its data: at least the caller's
      * own tasks, and possibly some other tasks
      * such as home that are known to not be sensitive.
      *
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index c7eb29f..2954234 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5732,6 +5732,7 @@
         // probably end up doing the same disk access.
         Application app;
         final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
+        final StrictMode.ThreadPolicy writesAllowedPolicy = StrictMode.getThreadPolicy();
         try {
             // If the app is being launched for full backup or restore, bring it up in
             // a restricted environment with the base application class.
@@ -5759,17 +5760,21 @@
                     "Exception thrown in onCreate() of "
                     + data.instrumentationName + ": " + e.toString(), e);
             }
+            try {
+                mInstrumentation.callApplicationOnCreate(app);
+            } catch (Exception e) {
+                if (!mInstrumentation.onException(app, e)) {
+                    throw new RuntimeException(
+                      "Unable to create application " + app.getClass().getName()
+                      + ": " + e.toString(), e);
+                }
+            }
         } finally {
-            StrictMode.setThreadPolicy(savedPolicy);
-        }
-
-        try {
-            mInstrumentation.callApplicationOnCreate(app);
-        } catch (Exception e) {
-            if (!mInstrumentation.onException(app, e)) {
-                throw new RuntimeException(
-                    "Unable to create application " + app.getClass().getName()
-                    + ": " + e.toString(), e);
+            // If the app targets < O-MR1, or doesn't change the thread policy
+            // during startup, clobber the policy to maintain behavior of b/36951662
+            if (data.appInfo.targetSdkVersion <= Build.VERSION_CODES.O
+                    || StrictMode.getThreadPolicy().equals(writesAllowedPolicy)) {
+                StrictMode.setThreadPolicy(savedPolicy);
             }
         }
 
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index b331d84..4bd85ae 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -252,8 +252,10 @@
     public static final int OP_INSTANT_APP_START_FOREGROUND = 68;
     /** @hide Answer incoming phone calls */
     public static final int OP_ANSWER_PHONE_CALLS = 69;
+    /** @hide Run jobs when in background */
+    public static final int OP_RUN_ANY_IN_BACKGROUND = 70;
     /** @hide */
-    public static final int _NUM_OP = 70;
+    public static final int _NUM_OP = 71;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -492,7 +494,8 @@
             OP_REQUEST_INSTALL_PACKAGES,
             OP_PICTURE_IN_PICTURE,
             OP_INSTANT_APP_START_FOREGROUND,
-            OP_ANSWER_PHONE_CALLS
+            OP_ANSWER_PHONE_CALLS,
+            OP_RUN_ANY_IN_BACKGROUND,
     };
 
     /**
@@ -570,6 +573,7 @@
             OPSTR_PICTURE_IN_PICTURE,
             OPSTR_INSTANT_APP_START_FOREGROUND,
             OPSTR_ANSWER_PHONE_CALLS,
+            null, // OP_RUN_ANY_IN_BACKGROUND
     };
 
     /**
@@ -647,6 +651,7 @@
             "PICTURE_IN_PICTURE",
             "INSTANT_APP_START_FOREGROUND",
             "ANSWER_PHONE_CALLS",
+            "RUN_ANY_IN_BACKGROUND",
     };
 
     /**
@@ -724,6 +729,7 @@
             null, // no permission for entering picture-in-picture on hide
             Manifest.permission.INSTANT_APP_FOREGROUND_SERVICE,
             Manifest.permission.ANSWER_PHONE_CALLS,
+            null, // no permission for OP_RUN_ANY_IN_BACKGROUND
     };
 
     /**
@@ -802,6 +808,7 @@
             null, // ENTER_PICTURE_IN_PICTURE_ON_HIDE
             null, // INSTANT_APP_START_FOREGROUND
             null, // ANSWER_PHONE_CALLS
+            null, // OP_RUN_ANY_IN_BACKGROUND
     };
 
     /**
@@ -879,6 +886,7 @@
             false, // ENTER_PICTURE_IN_PICTURE_ON_HIDE
             false, // INSTANT_APP_START_FOREGROUND
             false, // ANSWER_PHONE_CALLS
+            false, // OP_RUN_ANY_IN_BACKGROUND
     };
 
     /**
@@ -955,6 +963,7 @@
             AppOpsManager.MODE_ALLOWED,  // OP_PICTURE_IN_PICTURE
             AppOpsManager.MODE_DEFAULT,  // OP_INSTANT_APP_START_FOREGROUND
             AppOpsManager.MODE_ALLOWED, // ANSWER_PHONE_CALLS
+            AppOpsManager.MODE_ALLOWED,  // OP_RUN_ANY_IN_BACKGROUND
     };
 
     /**
@@ -1035,6 +1044,7 @@
             false, // OP_PICTURE_IN_PICTURE
             false,
             false, // ANSWER_PHONE_CALLS
+            false, // OP_RUN_ANY_IN_BACKGROUND
     };
 
     /**
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 7fc9a69..0eafdec 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -31,7 +31,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.ChangedPackages;
 import android.content.pm.ComponentInfo;
-import android.content.pm.InstantAppInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IOnPermissionsChangeListener;
 import android.content.pm.IPackageDataObserver;
@@ -40,6 +39,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
+import android.content.pm.InstantAppInfo;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.KeySet;
@@ -88,13 +88,14 @@
 import android.util.Log;
 import android.view.Display;
 
-import dalvik.system.VMRuntime;
-
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.UserIcons;
+
+import dalvik.system.VMRuntime;
+
 import libcore.util.EmptyArray;
 
 import java.lang.ref.WeakReference;
@@ -706,6 +707,15 @@
     }
 
     @Override
+    public String[] getNamesForUids(int[] uids) {
+        try {
+            return mPM.getNamesForUids(uids);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    @Override
     public int getUidForSharedUser(String sharedUserName)
             throws NameNotFoundException {
         try {
diff --git a/core/java/android/app/DexLoadReporter.java b/core/java/android/app/DexLoadReporter.java
index 371cd12..f99d1a8 100644
--- a/core/java/android/app/DexLoadReporter.java
+++ b/core/java/android/app/DexLoadReporter.java
@@ -19,6 +19,7 @@
 import android.os.FileUtils;
 import android.os.RemoteException;
 import android.os.SystemProperties;
+import android.system.ErrnoException;
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
@@ -26,6 +27,8 @@
 import dalvik.system.BaseDexClassLoader;
 import dalvik.system.VMRuntime;
 
+import libcore.io.Libcore;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -151,22 +154,50 @@
             // The dex path is not a secondary dex file. Nothing to do.
             return;
         }
-        File secondaryProfile = getSecondaryProfileFile(dexPath);
+
+        File realDexPath;
         try {
-            // Create the profile if not already there.
-            // Returns true if the file was created, false if the file already exists.
-            // or throws exceptions in case of errors.
+            // Secondary dex profiles are stored in the oat directory, next to the real dex file
+            // and have the same name with 'cur.prof' appended. We use the realpath because that
+            // is what installd is using when processing the dex file.
+            // NOTE: Keep in sync with installd.
+            realDexPath = new File(Libcore.os.realpath(dexPath));
+        } catch (ErrnoException ex) {
+            Slog.e(TAG, "Failed to get the real path of secondary dex " + dexPath
+                    + ":" + ex.getMessage());
+            // Do not continue with registration if we could not retrieve the real path.
+            return;
+        }
+
+        // NOTE: Keep this in sync with installd expectations.
+        File secondaryProfileDir = new File(realDexPath.getParent(), "oat");
+        File secondaryProfile = new File(secondaryProfileDir, realDexPath.getName() + ".cur.prof");
+
+        // Create the profile if not already there.
+        // Returns true if the file was created, false if the file already exists.
+        // or throws exceptions in case of errors.
+        if (!secondaryProfileDir.exists()) {
+            if (!secondaryProfileDir.mkdir()) {
+                Slog.e(TAG, "Could not create the profile directory: " + secondaryProfile);
+                // Do not continue with registration if we could not create the oat dir.
+                return;
+            }
+        }
+
+        try {
             boolean created = secondaryProfile.createNewFile();
             if (DEBUG && created) {
                 Slog.i(TAG, "Created profile for secondary dex: " + secondaryProfile);
             }
         } catch (IOException ex) {
-            Slog.e(TAG, "Failed to create profile for secondary dex " + secondaryProfile +
-                    ":" + ex.getMessage());
-            // Don't move forward with the registration if we failed to create the profile.
+            Slog.e(TAG, "Failed to create profile for secondary dex " + dexPath
+                    + ":" + ex.getMessage());
+            // Do not continue with registration if we could not create the profile files.
             return;
         }
 
+        // If we got here, the dex paths is a secondary dex and we were able to create the profile.
+        // Register the path to the runtime.
         VMRuntime.registerAppInfo(secondaryProfile.getPath(), new String[] { dexPath });
     }
 
@@ -180,11 +211,4 @@
         }
         return false;
     }
-
-    // Secondary dex profiles are stored next to the dex file and have the same
-    // name with '.prof' appended.
-    // NOTE: Keep in sync with installd.
-    private File getSecondaryProfileFile(String dexPath) {
-        return new File(dexPath + ".prof");
-    }
 }
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 0e33934..49d58eb 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -144,15 +144,15 @@
      * or {@link WallpaperManager#FLAG_SYSTEM}
      * @return colors of chosen wallpaper
      */
-    WallpaperColors getWallpaperColors(int which);
+    WallpaperColors getWallpaperColors(int which, int userId);
 
     /**
      * Register a callback to receive color updates
      */
-    void registerWallpaperColorsCallback(IWallpaperManagerCallback cb);
+    void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId);
 
     /**
      * Unregister a callback that was receiving color updates
      */
-    void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb);
+    void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId);
 }
diff --git a/core/java/android/app/IWallpaperManagerCallback.aidl b/core/java/android/app/IWallpaperManagerCallback.aidl
index 0cfbaef..ea0ceab 100644
--- a/core/java/android/app/IWallpaperManagerCallback.aidl
+++ b/core/java/android/app/IWallpaperManagerCallback.aidl
@@ -34,6 +34,6 @@
     /**
      * Called when wallpaper colors change
      */
-    void onWallpaperColorsChanged(in WallpaperColors colors, int which);
+    void onWallpaperColorsChanged(in WallpaperColors colors, int which, int userId);
 
 }
diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java
index e4a22c4..95ec24c 100644
--- a/core/java/android/app/IntentService.java
+++ b/core/java/android/app/IntentService.java
@@ -43,6 +43,13 @@
  * long as necessary (and will not block the application's main loop), but
  * only one request will be processed at a time.
  *
+ * <p class="note"><b>Note:</b> IntentService is subject to all the
+ * <a href="/preview/features/background.html">background execution limits</a>
+ * imposed with Android 8.0 (API level 26). In most cases, you are better off
+ * using {@link android.support.v4.app.JobIntentService}, which uses jobs
+ * instead of services when running on Android 8.0 or higher.
+ * </p>
+ *
  * <div class="special reference">
  * <h3>Developer Guides</h3>
  * <p>For a detailed discussion about how to create services, read the
@@ -50,6 +57,7 @@
  * guide.</p>
  * </div>
  *
+ * @see android.support.v4.app.JobIntentService
  * @see android.os.AsyncTask
  */
 public abstract class IntentService extends Service {
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index c0381d6..76643d6 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -298,7 +298,9 @@
     }
 
     /**
-     * Callback passed to {@link KeyguardManager#dismissKeyguard} to notify caller of result.
+     * Callback passed to
+     * {@link KeyguardManager#requestDismissKeyguard(Activity, KeyguardDismissCallback)}
+     * to notify caller of result.
      */
     public static abstract class KeyguardDismissCallback {
 
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 8858172..34343e9 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -204,7 +204,12 @@
     public static final int IMPORTANCE_NONE = 0;
 
     /**
-     * Min notification importance: only shows in the shade, below the fold.
+     * Min notification importance: only shows in the shade, below the fold.  This should
+     * not be used with {@link Service#startForeground(int, Notification) Service.startForeground}
+     * since a foreground service is supposed to be something the user cares about so it does
+     * not make semantic sense to mark its notification as minimum importance.  If you do this
+     * as of Android version {@link android.os.Build.VERSION_CODES#O}, the system will show
+     * a higher-priority notification about your app running in the background.
      */
     public static final int IMPORTANCE_MIN = 1;
 
diff --git a/core/java/android/app/WallpaperColors.java b/core/java/android/app/WallpaperColors.java
index d0791cf..2a8130f 100644
--- a/core/java/android/app/WallpaperColors.java
+++ b/core/java/android/app/WallpaperColors.java
@@ -408,4 +408,13 @@
 
         return new Size(newWidth, newHeight);
     }
+
+    @Override
+    public String toString() {
+        final StringBuilder colors = new StringBuilder();
+        for (int i = 0; i < mMainColors.size(); i++) {
+            colors.append(Integer.toHexString(mMainColors.get(i).toArgb())).append(" ");
+        }
+        return "[WallpaperColors: " + colors.toString() + "h: " + mColorHints + "]";
+    }
 }
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 0398b47..16d0214 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -307,13 +307,14 @@
          * changes its colors.
          * @param callback Listener
          * @param handler Thread to call it from. Main thread if null.
+         * @param userId Owner of the wallpaper or UserHandle.USER_ALL
          */
         public void addOnColorsChangedListener(@NonNull OnColorsChangedListener callback,
-                @Nullable Handler handler) {
+                @Nullable Handler handler, int userId) {
             synchronized (this) {
                 if (!mColorCallbackRegistered) {
                     try {
-                        mService.registerWallpaperColorsCallback(this);
+                        mService.registerWallpaperColorsCallback(this, userId);
                         mColorCallbackRegistered = true;
                     } catch (RemoteException e) {
                         // Failed, service is gone
@@ -328,15 +329,17 @@
          * Stop listening to wallpaper color events.
          *
          * @param callback listener
+         * @param userId Owner of the wallpaper or UserHandle.USER_ALL
          */
-        public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback) {
+        public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback,
+                int userId) {
             synchronized (this) {
                 mColorListeners.removeIf(pair -> pair.first == callback);
 
                 if (mColorListeners.size() == 0 && mColorCallbackRegistered) {
                     mColorCallbackRegistered = false;
                     try {
-                        mService.unregisterWallpaperColorsCallback(this);
+                        mService.unregisterWallpaperColorsCallback(this, userId);
                     } catch (RemoteException e) {
                         // Failed, service is gone
                         Log.w(TAG, "Can't unregister color updates", e);
@@ -346,7 +349,7 @@
         }
 
         @Override
-        public void onWallpaperColorsChanged(WallpaperColors colors, int which) {
+        public void onWallpaperColorsChanged(WallpaperColors colors, int which, int userId) {
             synchronized (this) {
                 for (Pair<OnColorsChangedListener, Handler> listener : mColorListeners) {
                     Handler handler = listener.second;
@@ -361,21 +364,21 @@
                             stillExists = mColorListeners.contains(listener);
                         }
                         if (stillExists) {
-                            listener.first.onColorsChanged(colors, which);
+                            listener.first.onColorsChanged(colors, which, userId);
                         }
                     });
                 }
             }
         }
 
-        WallpaperColors getWallpaperColors(int which) {
+        WallpaperColors getWallpaperColors(int which, int userId) {
             if (which != FLAG_LOCK && which != FLAG_SYSTEM) {
                 throw new IllegalArgumentException(
                         "Must request colors for exactly one kind of wallpaper");
             }
 
             try {
-                return mService.getWallpaperColors(which);
+                return mService.getWallpaperColors(which, userId);
             } catch (RemoteException e) {
                 // Can't get colors, connection lost.
             }
@@ -857,7 +860,7 @@
      * @param listener A listener to register
      */
     public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener) {
-        sGlobals.addOnColorsChangedListener(listener, null);
+        addOnColorsChangedListener(listener, null);
     }
 
     /**
@@ -868,25 +871,61 @@
      */
     public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener,
             @NonNull Handler handler) {
-        sGlobals.addOnColorsChangedListener(listener, handler);
+        addOnColorsChangedListener(listener, handler, mContext.getUserId());
+    }
+
+    /**
+     * Registers a listener to get notified when the wallpaper colors change
+     * @param listener A listener to register
+     * @param handler Where to call it from. Will be called from the main thread
+     *                if null.
+     * @param userId Owner of the wallpaper or UserHandle.USER_ALL.
+     * @hide
+     */
+    public void addOnColorsChangedListener(@NonNull OnColorsChangedListener listener,
+            @NonNull Handler handler, int userId) {
+        sGlobals.addOnColorsChangedListener(listener, handler, userId);
     }
 
     /**
      * Stop listening to color updates.
-     * @param callback A callback to unsubscribe
+     * @param callback A callback to unsubscribe.
      */
     public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback) {
-        sGlobals.removeOnColorsChangedListener(callback);
+        removeOnColorsChangedListener(callback, mContext.getUserId());
+    }
+
+    /**
+     * Stop listening to color updates.
+     * @param callback A callback to unsubscribe.
+     * @param userId Owner of the wallpaper or UserHandle.USER_ALL.
+     * @hide
+     */
+    public void removeOnColorsChangedListener(@NonNull OnColorsChangedListener callback,
+            int userId) {
+        sGlobals.removeOnColorsChangedListener(callback, userId);
     }
 
     /**
      * Get the primary colors of a wallpaper
      * @param which wallpaper type. Must be either {@link #FLAG_SYSTEM} or
      *     {@link #FLAG_LOCK}
-     * @return a list of colors ordered by priority
+     * @return {@link WallpaperColors} or null if colors are unknown.
      */
     public @Nullable WallpaperColors getWallpaperColors(int which) {
-        return sGlobals.getWallpaperColors(which);
+        return getWallpaperColors(which, mContext.getUserId());
+    }
+
+    /**
+     * Get the primary colors of a wallpaper
+     * @param which wallpaper type. Must be either {@link #FLAG_SYSTEM} or
+     *     {@link #FLAG_LOCK}
+     * @param userId Owner of the wallpaper.
+     * @return {@link WallpaperColors} or null if colors are unknown.
+     * @hide
+     */
+    public @Nullable WallpaperColors getWallpaperColors(int which, int userId) {
+        return sGlobals.getWallpaperColors(which, userId);
     }
 
     /**
@@ -1902,9 +1941,9 @@
         }
 
         @Override
-        public void onWallpaperColorsChanged(WallpaperColors colors, int which)
+        public void onWallpaperColorsChanged(WallpaperColors colors, int which, int userId)
             throws RemoteException {
-            sGlobals.onWallpaperColorsChanged(colors, which);
+            sGlobals.onWallpaperColorsChanged(colors, which, userId);
         }
     }
 
@@ -1921,5 +1960,19 @@
          * @param which A combination of {@link #FLAG_LOCK} and {@link #FLAG_SYSTEM}
          */
         void onColorsChanged(WallpaperColors colors, int which);
+
+        /**
+         * Called when colors change.
+         * A {@link android.app.WallpaperColors} object containing a simplified
+         * color histogram will be given.
+         *
+         * @param colors Wallpaper color info
+         * @param which A combination of {@link #FLAG_LOCK} and {@link #FLAG_SYSTEM}
+         * @param userId Owner of the wallpaper
+         * @hide
+         */
+        default void onColorsChanged(WallpaperColors colors, int which, int userId) {
+            onColorsChanged(colors, which);
+        }
     }
 }
diff --git a/core/java/android/app/backup/WallpaperBackupHelper.java b/core/java/android/app/backup/WallpaperBackupHelper.java
index f987468..36f5f96 100644
--- a/core/java/android/app/backup/WallpaperBackupHelper.java
+++ b/core/java/android/app/backup/WallpaperBackupHelper.java
@@ -18,20 +18,19 @@
 
 import android.app.WallpaperManager;
 import android.content.Context;
-import android.graphics.BitmapFactory;
-import android.graphics.Point;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.os.UserHandle;
 import android.util.Slog;
-import android.view.Display;
-import android.view.WindowManager;
 
 import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
 
 /**
- * Helper for backing up / restoring wallpapers.  Basically an AbsoluteFileBackupHelper,
- * but with logic for deciding what to do with restored wallpaper images.
+ * We no longer back up wallpapers with this helper, but we do need to process restores
+ * of legacy backup payloads.  We just take the restored image as-is and apply it as the
+ * system wallpaper using the public "set the wallpaper" API.
  *
  * @hide
  */
@@ -39,83 +38,34 @@
     private static final String TAG = "WallpaperBackupHelper";
     private static final boolean DEBUG = false;
 
-    // If 'true', then apply an acceptable-size heuristic at restore time, dropping back
-    // to the factory default wallpaper if the restored one differs "too much" from the
-    // device's preferred wallpaper image dimensions.
-    private static final boolean REJECT_OUTSIZED_RESTORE = false;
-
-    // When outsized restore rejection is enabled, this is the maximum ratio between the
-    // source and target image heights that will be permitted.  The ratio is checked both
-    // ways (i.e. >= MAX, or <= 1/MAX) to validate restores from both largeer-than-target
-    // and smaller-than-target sources.
-    private static final double MAX_HEIGHT_RATIO = 1.35;
-
-    // The height ratio check when applying larger images on smaller screens is separate;
-    // in current policy we accept any such restore regardless of the relative dimensions.
-    private static final double MIN_HEIGHT_RATIO = 0;
-
-    // This path must match what the WallpaperManagerService uses
-    // TODO: Will need to change if backing up non-primary user's wallpaper
-    // http://b/22388012
-    public static final String WALLPAPER_IMAGE =
-            new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM),
-                    "wallpaper").getAbsolutePath();
-    public static final String WALLPAPER_ORIG_IMAGE =
-            new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM),
-                    "wallpaper_orig").getAbsolutePath();
-    public static final String WALLPAPER_INFO =
-            new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM),
-                    "wallpaper_info.xml").getAbsolutePath();
-    // Use old keys to keep legacy data compatibility and avoid writing two wallpapers
+    // Key that legacy wallpaper imagery was stored under
     public static final String WALLPAPER_IMAGE_KEY =
             "/data/data/com.android.settings/files/wallpaper";
     public static final String WALLPAPER_INFO_KEY = "/data/system/wallpaper_info.xml";
 
-    // Stage file - should be adjacent to the WALLPAPER_IMAGE location.  The wallpapers
-    // will be saved to this file from the restore stream, then renamed to the proper
-    // location if it's deemed suitable.
-    // TODO: Will need to change if backing up non-primary user's wallpaper
-    // http://b/22388012
+    // Stage file that the restored imagery is stored to prior to being applied
+    // as the system wallpaper.
     private static final String STAGE_FILE =
             new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM),
                     "wallpaper-tmp").getAbsolutePath();
 
-    Context mContext;
-    String[] mFiles;
-    String[] mKeys;
-    double mDesiredMinWidth;
-    double mDesiredMinHeight;
+    private final String[] mKeys;
+    private final WallpaperManager mWpm;
 
     /**
-     * Construct a helper for backing up / restoring the files at the given absolute locations
-     * within the file system.
+     * Legacy wallpaper restores, from back when the imagery was stored under the
+     * "android" system package as file key/value entities.
      *
      * @param context
      * @param files
      */
-    public WallpaperBackupHelper(Context context, String[] files, String[] keys) {
+    public WallpaperBackupHelper(Context context, String[] keys) {
         super(context);
 
         mContext = context;
-        mFiles = files;
         mKeys = keys;
 
-        final WindowManager wm =
-                (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
-        final WallpaperManager wpm =
-                (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);
-        final Display d = wm.getDefaultDisplay();
-        final Point size = new Point();
-        d.getSize(size);
-        mDesiredMinWidth = Math.min(size.x, size.y);
-        mDesiredMinHeight = (double) wpm.getDesiredMinimumHeight();
-        if (mDesiredMinHeight <= 0) {
-            mDesiredMinHeight = size.y;
-        }
-
-        if (DEBUG) {
-            Slog.d(TAG, "dmW=" + mDesiredMinWidth + " dmH=" + mDesiredMinHeight);
-        }
+        mWpm = (WallpaperManager) context.getSystemService(Context.WALLPAPER_SERVICE);
     }
 
     /**
@@ -126,13 +76,12 @@
     @Override
     public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
             ParcelFileDescriptor newState) {
-        performBackup_checked(oldState, data, newState, mFiles, mKeys);
+        // Intentionally no-op; we don't back up the wallpaper this way any more.
     }
 
     /**
      * Restore one absolute file entity from the restore stream.  If we're restoring the
-     * magic wallpaper file, take specific action to determine whether it is suitable for
-     * the current device.
+     * magic wallpaper file, apply it as the system wallpaper.
      */
     @Override
     public void restoreEntity(BackupDataInputStream data) {
@@ -140,69 +89,21 @@
         if (isKeyInList(key, mKeys)) {
             if (key.equals(WALLPAPER_IMAGE_KEY)) {
                 // restore the file to the stage for inspection
-                File f = new File(STAGE_FILE);
-                if (writeFile(f, data)) {
-
-                    // Preflight the restored image's dimensions without loading it
-                    BitmapFactory.Options options = new BitmapFactory.Options();
-                    options.inJustDecodeBounds = true;
-                    BitmapFactory.decodeFile(STAGE_FILE, options);
-
-                    if (DEBUG) Slog.d(TAG, "Restoring wallpaper image w=" + options.outWidth
-                            + " h=" + options.outHeight);
-
-                    if (REJECT_OUTSIZED_RESTORE) {
-                        // We accept any wallpaper that is at least as wide as our preference
-                        // (i.e. wide enough to fill the screen), and is within a comfortable
-                        // factor of the target height, to avoid significant clipping/scaling/
-                        // letterboxing.  At this point we know that mDesiredMinWidth is the
-                        // smallest dimension, regardless of current orientation, so we can
-                        // safely require that the candidate's width and height both exceed
-                        // that hard minimum.
-                        final double heightRatio = mDesiredMinHeight / options.outHeight;
-                        if (options.outWidth < mDesiredMinWidth
-                                || options.outHeight < mDesiredMinWidth
-                                || heightRatio >= MAX_HEIGHT_RATIO
-                                || heightRatio <= MIN_HEIGHT_RATIO) {
-                            // Not wide enough for the screen, or too short/tall to be a good fit
-                            // for the height of the screen, broken image file, or the system's
-                            // desires for wallpaper size are in a bad state.  Probably one of the
-                            // first two.
-                            Slog.i(TAG, "Restored image dimensions (w="
-                                    + options.outWidth + ", h=" + options.outHeight
-                                    + ") too far off target (tw="
-                                    + mDesiredMinWidth + ", th=" + mDesiredMinHeight
-                                    + "); falling back to default wallpaper.");
-                            f.delete();
-                            return;
+                File stage = new File(STAGE_FILE);
+                try {
+                    if (writeFile(stage, data)) {
+                        try (FileInputStream in = new FileInputStream(stage)) {
+                            mWpm.setStream(in);
+                        } catch (IOException e) {
+                            Slog.e(TAG, "Unable to set restored wallpaper: " + e.getMessage());
                         }
+                    } else {
+                        Slog.e(TAG, "Unable to save restored wallpaper");
                     }
-
-                    // We passed the acceptable-dimensions test (if any), so we're going to
-                    // use the restored image.  That comes last, when we are done restoring
-                    // both the pixels and the metadata.
+                } finally {
+                    stage.delete();
                 }
-            } else if (key.equals(WALLPAPER_INFO_KEY)) {
-                // XML file containing wallpaper info
-                File f = new File(WALLPAPER_INFO);
-                writeFile(f, data);
             }
         }
     }
-
-    /**
-     * Hook for the agent to call this helper upon completion of the restore.  We do this
-     * upon completion so that we know both the imagery and the wallpaper info have
-     * been emplaced without requiring either or relying on ordering.
-     */
-    public void onRestoreFinished() {
-        final File f = new File(STAGE_FILE);
-        if (f.exists()) {
-            // TODO: spin a service to copy the restored image to sd/usb storage,
-            // since it does not exist anywhere other than the private wallpaper
-            // file.
-            Slog.d(TAG, "Applying restored wallpaper image.");
-            f.renameTo(new File(WALLPAPER_ORIG_IMAGE));
-        }
-    }
 }
diff --git a/core/java/android/app/usage/StorageStatsManager.java b/core/java/android/app/usage/StorageStatsManager.java
index 7c68079..3d187ec 100644
--- a/core/java/android/app/usage/StorageStatsManager.java
+++ b/core/java/android/app/usage/StorageStatsManager.java
@@ -119,8 +119,7 @@
      * could be reclaimed by the system.
      * <p>
      * Apps making logical decisions about disk space should always use
-     * {@link StorageManager#getAllocatableBytes(UUID, int)} instead of this
-     * value.
+     * {@link StorageManager#getAllocatableBytes(UUID)} instead of this value.
      *
      * @param storageUuid the UUID of the storage volume you're interested in,
      *            such as {@link StorageManager#UUID_DEFAULT}.
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index 076deab..b2952aa 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -113,8 +113,8 @@
      *
      * <p>If your app needs to be excluded from battery optimizations (run in the background)
      * or to have unrestricted data access (use data in the background) you can declare that
-     * you use the {@link android.Manifest.permission#RUN_IN_BACKGROUND} and {@link
-     * android.Manifest.permission#USE_DATA_IN_BACKGROUND} respectively. Note that these
+     * you use the {@link android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND} and {@link
+     * android.Manifest.permission#REQUEST_COMPANION_USE_DATA_IN_BACKGROUND} respectively. Note that these
      * special capabilities have a negative effect on the device's battery and user's data
      * usage, therefore you should requested them when absolutely necessary.</p>
      *
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index f907721..58a9183 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -338,7 +338,7 @@
      * before they system will consider them non-responsive and ANR the app.  Since these usually
      * execute on the app's main thread, they are already bound by the ~5 second time limit
      * of various operations that can happen there (not to mention just avoiding UI jank), so
-     * the receive limit is generally not of concern.  However, once you use {@goAsync}, though
+     * the receive limit is generally not of concern.  However, once you use {@code goAsync}, though
      * able to be off the main thread, the broadcast execution limit still applies, and that
      * includes the time spent between calling this method and ultimately
      * {@link PendingResult#finish() PendingResult.finish()}.</p>
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 64e464c..cdeaea3 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -39,7 +39,6 @@
 import android.os.CancellationSignal;
 import android.os.IBinder;
 import android.os.ICancellationSignal;
-import android.os.OperationCanceledException;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
@@ -47,7 +46,6 @@
 import android.os.storage.StorageManager;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.MathUtils;
 
 import java.io.File;
 import java.io.FileDescriptor;
@@ -1047,7 +1045,7 @@
      * @param sortOrder How the rows in the cursor should be sorted.
      *      If {@code null} then the provider is free to define the sort order.
      * @param cancellationSignal A signal to cancel the operation in progress, or {@code null} if none.
-     * If the operation is canceled, then {@link OperationCanceledException} will be thrown
+     * If the operation is canceled, then {@link android.os.OperationCanceledException} will be thrown
      * when the query is executed.
      * @return a Cursor or {@code null}.
      */
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index 2a3fac3..0b16852 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -32,6 +32,7 @@
     void removeSplit(String splitName);
 
     void close();
-    void commit(in IntentSender statusReceiver);
+    void commit(in IntentSender statusReceiver, boolean forTransferred);
+    void transfer(in String packageName);
     void abandon();
 }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index c9afd6b..0e70645 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -128,6 +128,7 @@
     String[] getPackagesForUid(int uid);
 
     String getNameForUid(int uid);
+    String[] getNamesForUids(in int[] uids);
 
     int getUidForSharedUser(String sharedUserName);
 
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index ea675fb..f4fdcaa 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -38,6 +38,7 @@
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
 import android.os.Parcelable;
+import android.os.ParcelableException;
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.system.ErrnoException;
@@ -793,7 +794,7 @@
          * @throws IOException if trouble opening the file for writing, such as
          *             lack of disk space or unavailable media.
          * @throws SecurityException if called after the session has been
-         *             committed or abandoned.
+         *             sealed or abandoned
          */
         public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes,
                 long lengthBytes) throws IOException {
@@ -918,7 +919,68 @@
          */
         public void commit(@NonNull IntentSender statusReceiver) {
             try {
-                mSession.commit(statusReceiver);
+                mSession.commit(statusReceiver, false);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Attempt to commit a session that has been {@link #transfer(String) transferred}.
+         *
+         * <p>If the device reboots before the session has been finalized, you may commit the
+         * session again.
+         *
+         * <p>The caller of this method is responsible to ensure the safety of the session. As the
+         * session was created by another - usually less trusted - app, it is paramount that before
+         * committing <u>all</u> public and system {@link SessionInfo properties of the session}
+         * and <u>all</u> {@link #openRead(String) APKs} are verified by the caller. It might happen
+         * that new properties are added to the session with a new API revision. In this case the
+         * callers need to be updated.
+         *
+         * @param statusReceiver Callbacks called when the state of the session changes.
+         *
+         * @hide
+         */
+        @SystemApi
+        @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES)
+        public void commitTransferred(@NonNull IntentSender statusReceiver) {
+            try {
+                mSession.commit(statusReceiver, true);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
+         * Transfer the session to a new owner.
+         * <p>
+         * Only sessions that update the installing app can be transferred.
+         * <p>
+         * After the transfer to a package with a different uid all method calls on the session
+         * will cause {@link SecurityException}s.
+         * <p>
+         * Once this method is called, the session is sealed and no additional mutations beside
+         * committing it may be performed on the session.
+         *
+         * @param packageName The package of the new owner. Needs to hold the INSTALL_PACKAGES
+         *                    permission.
+         *
+         * @throws PackageManager.NameNotFoundException if the new owner could not be found.
+         * @throws SecurityException if called after the session has been committed or abandoned.
+         * @throws SecurityException if the session does not update the original installer
+         * @throws SecurityException if streams opened through
+         *                           {@link #openWrite(String, long, long) are still open.
+         */
+        public void transfer(@NonNull String packageName)
+                throws PackageManager.NameNotFoundException {
+            Preconditions.checkNotNull(packageName);
+
+            try {
+                mSession.transfer(packageName);
+            } catch (ParcelableException e) {
+                e.maybeRethrow(PackageManager.NameNotFoundException.class);
+                throw new RuntimeException(e);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
             }
@@ -1041,6 +1103,26 @@
         }
 
         /**
+         * Check if there are hidden options set.
+         *
+         * <p>Hidden options are those options that cannot be verified via public or system-api
+         * methods on {@link SessionInfo}.
+         *
+         * @return {@code true} if any hidden option is set.
+         *
+         * @hide
+         */
+        public boolean areHiddenOptionsSet() {
+            return (installFlags & (PackageManager.INSTALL_ALLOW_DOWNGRADE
+                    | PackageManager.INSTALL_DONT_KILL_APP
+                    | PackageManager.INSTALL_INSTANT_APP
+                    | PackageManager.INSTALL_FULL_APP
+                    | PackageManager.INSTALL_VIRTUAL_PRELOAD
+                    | PackageManager.INSTALL_ALLOCATE_AGGRESSIVE)) != installFlags
+                    || abiOverride != null || volumeUuid != null;
+        }
+
+        /**
          * Provide value of {@link PackageInfo#installLocation}, which may be used
          * to determine where the app will be staged. Defaults to
          * {@link PackageInfo#INSTALL_LOCATION_INTERNAL_ONLY}.
@@ -1300,6 +1382,19 @@
         public CharSequence appLabel;
 
         /** {@hide} */
+        public int installLocation;
+        /** {@hide} */
+        public Uri originatingUri;
+        /** {@hide} */
+        public int originatingUid;
+        /** {@hide} */
+        public Uri referrerUri;
+        /** {@hide} */
+        public String[] grantedRuntimePermissions;
+        /** {@hide} */
+        public int installFlags;
+
+        /** {@hide} */
         public SessionInfo() {
         }
 
@@ -1318,6 +1413,13 @@
             appPackageName = source.readString();
             appIcon = source.readParcelable(null);
             appLabel = source.readString();
+
+            installLocation = source.readInt();
+            originatingUri = source.readParcelable(null);
+            originatingUid = source.readInt();
+            referrerUri = source.readParcelable(null);
+            grantedRuntimePermissions = source.readStringArray();
+            installFlags = source.readInt();
         }
 
         /**
@@ -1406,8 +1508,9 @@
                 // Icon may have been omitted for calls that return bulk session
                 // lists, so try fetching the specific icon.
                 try {
-                    appIcon = AppGlobals.getPackageManager().getPackageInstaller()
-                            .getSessionInfo(sessionId).appIcon;
+                    final SessionInfo info = AppGlobals.getPackageManager().getPackageInstaller()
+                            .getSessionInfo(sessionId);
+                    appIcon = (info != null) ? info.appIcon : null;
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 }
@@ -1440,6 +1543,130 @@
             return intent;
         }
 
+        /**
+         * Get the mode of the session as set in the constructor of the {@link SessionParams}.
+         *
+         * @return One of {@link SessionParams#MODE_FULL_INSTALL}
+         *         or {@link SessionParams#MODE_INHERIT_EXISTING}
+         */
+        public int getMode() {
+            return mode;
+        }
+
+        /**
+         * Get the value set in {@link SessionParams#setInstallLocation(int)}.
+         */
+        public int getInstallLocation() {
+            return installLocation;
+        }
+
+        /**
+         * Get the value as set in {@link SessionParams#setSize(long)}.
+         *
+         * <p>The value is a hint and does not have to match the actual size.
+         */
+        public long getSize() {
+            return sizeBytes;
+        }
+
+        /**
+         * Get the value set in {@link SessionParams#setOriginatingUri(Uri)}.
+         */
+        public @Nullable Uri getOriginatingUri() {
+            return originatingUri;
+        }
+
+        /**
+         * Get the value set in {@link SessionParams#setOriginatingUid(int)}.
+         */
+        public int getOriginatingUid() {
+            return originatingUid;
+        }
+
+        /**
+         * Get the value set in {@link SessionParams#setReferrerUri(Uri)}
+         */
+        public @Nullable Uri getReferrerUri() {
+            return referrerUri;
+        }
+
+        /**
+         * Get the value set in {@link SessionParams#setGrantedRuntimePermissions(String[])}.
+         *
+         * @hide
+         */
+        @SystemApi
+        public @Nullable String[] getGrantedRuntimePermissions() {
+            return grantedRuntimePermissions;
+        }
+
+        /**
+         * Get the value set in {@link SessionParams#setAllowDowngrade(boolean)}.
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean getAllowDowngrade() {
+            return (installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) != 0;
+        }
+
+        /**
+         * Get the value set in {@link SessionParams#setDontKillApp(boolean)}.
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean getDontKillApp() {
+            return (installFlags & PackageManager.INSTALL_DONT_KILL_APP) != 0;
+        }
+
+        /**
+         * If {@link SessionParams#setInstallAsInstantApp(boolean)} was called with {@code true},
+         * return true. If it was called with {@code false} or if it was not called return false.
+         *
+         * @hide
+         *
+         * @see #getInstallAsFullApp
+         */
+        @SystemApi
+        public boolean getInstallAsInstantApp(boolean isInstantApp) {
+            return (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
+        }
+
+        /**
+         * If {@link SessionParams#setInstallAsInstantApp(boolean)} was called with {@code false},
+         * return true. If it was called with {@code true} or if it was not called return false.
+         *
+         * @hide
+         *
+         * @see #getInstallAsInstantApp
+         */
+        @SystemApi
+        public boolean getInstallAsFullApp(boolean isInstantApp) {
+            return (installFlags & PackageManager.INSTALL_FULL_APP) != 0;
+        }
+
+        /**
+         * Get if {@link SessionParams#setInstallAsVirtualPreload()} was called.
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean getInstallAsVirtualPreload() {
+            return (installFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0;
+        }
+
+        /**
+         * Get the value set in {@link SessionParams#setAllocateAggressive(boolean)}.
+         *
+         * @hide
+         */
+        @SystemApi
+        public boolean getAllocateAggressive() {
+            return (installFlags & PackageManager.INSTALL_ALLOCATE_AGGRESSIVE) != 0;
+        }
+
+
         /** {@hide} */
         @Deprecated
         public @Nullable Intent getDetailsIntent() {
@@ -1466,6 +1693,13 @@
             dest.writeString(appPackageName);
             dest.writeParcelable(appIcon, flags);
             dest.writeString(appLabel != null ? appLabel.toString() : null);
+
+            dest.writeInt(installLocation);
+            dest.writeParcelable(originatingUri, flags);
+            dest.writeInt(originatingUid);
+            dest.writeParcelable(referrerUri, flags);
+            dest.writeStringArray(grantedRuntimePermissions);
+            dest.writeInt(installFlags);
         }
 
         public static final Parcelable.Creator<SessionInfo>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 175293d..f6eaed4 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3590,6 +3590,14 @@
     public abstract @Nullable String getNameForUid(int uid);
 
     /**
+     * Retrieves the official names associated with each given uid.
+     * @see #getNameForUid(int)
+     *
+     * @hide
+     */
+    public abstract @Nullable String[] getNamesForUids(int[] uids);
+
+    /**
      * Return the user id associated with a shared user name. Multiple
      * applications can specify a shared user name in their manifest and thus
      * end up using a common uid. This might be used for new applications
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e2f55ad..dc171f5 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -50,6 +50,8 @@
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageParserCacheHelper.ReadHelper;
+import android.content.pm.PackageParserCacheHelper.WriteHelper;
 import android.content.pm.split.DefaultSplitAssetLoader;
 import android.content.pm.split.SplitAssetDependencyLoader;
 import android.content.pm.split.SplitAssetLoader;
@@ -121,6 +123,7 @@
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.zip.ZipEntry;
 
@@ -152,9 +155,8 @@
     private static final String PROPERTY_CHILD_PACKAGES_ENABLED =
             "persist.sys.child_packages_enabled";
 
-    // TODO: Decide the correct default before O-MR1.
     private static final boolean MULTI_PACKAGE_APK_ENABLED = Build.IS_DEBUGGABLE &&
-            SystemProperties.getBoolean(PROPERTY_CHILD_PACKAGES_ENABLED, true);
+            SystemProperties.getBoolean(PROPERTY_CHILD_PACKAGES_ENABLED, false);
 
     private static final int MAX_PACKAGES_PER_APK = 5;
 
@@ -235,6 +237,11 @@
 
     private static final boolean LOG_UNSAFE_BROADCASTS = false;
 
+    /**
+     * Total number of packages that were read from the cache.  We use it only for logging.
+     */
+    public static final AtomicInteger sCachedPackageReadCount = new AtomicInteger();
+
     // Set of broadcast actions that are safe for manifest receivers
     private static final Set<String> SAFE_BROADCASTS = new ArraySet<>();
     static {
@@ -1035,21 +1042,45 @@
     }
 
     @VisibleForTesting
-    protected Package fromCacheEntry(byte[] bytes) throws IOException {
-        Parcel p = Parcel.obtain();
+    protected Package fromCacheEntry(byte[] bytes) {
+        return fromCacheEntryStatic(bytes);
+    }
+
+    /** static version of {@link #fromCacheEntry} for unit tests. */
+    @VisibleForTesting
+    public static Package fromCacheEntryStatic(byte[] bytes) {
+        final Parcel p = Parcel.obtain();
         p.unmarshall(bytes, 0, bytes.length);
         p.setDataPosition(0);
 
+        final ReadHelper helper = new ReadHelper(p);
+        helper.startAndInstall();
+
         PackageParser.Package pkg = new PackageParser.Package(p);
+
         p.recycle();
 
+        sCachedPackageReadCount.incrementAndGet();
+
         return pkg;
     }
 
     @VisibleForTesting
-    protected byte[] toCacheEntry(Package pkg) throws IOException {
-        Parcel p = Parcel.obtain();
+    protected byte[] toCacheEntry(Package pkg) {
+        return toCacheEntryStatic(pkg);
+
+    }
+
+    /** static version of {@link #toCacheEntry} for unit tests. */
+    @VisibleForTesting
+    public static byte[] toCacheEntryStatic(Package pkg) {
+        final Parcel p = Parcel.obtain();
+        final WriteHelper helper = new WriteHelper(p);
+
         pkg.writeToParcel(p, 0 /* flags */);
+
+        helper.finishAndUninstall();
+
         byte[] serialized = p.marshall();
         p.recycle();
 
@@ -1146,13 +1177,7 @@
             }
         }
 
-        final byte[] cacheEntry;
-        try {
-            cacheEntry = toCacheEntry(parsed);
-        } catch (IOException ioe) {
-            Slog.e(TAG, "Unable to serialize parsed package for: " + packageFile);
-            return;
-        }
+        final byte[] cacheEntry = toCacheEntry(parsed);
 
         if (cacheEntry == null) {
             return;
@@ -6427,8 +6452,11 @@
 
             dest.writeStringList(requestedPermissions);
             dest.writeStringList(protectedBroadcasts);
+
+            // TODO: This doesn't work: b/64295061
             dest.writeParcelable(parentPackage, flags);
             dest.writeParcelableList(childPackages, flags);
+
             dest.writeString(staticSharedLibName);
             dest.writeInt(staticSharedLibVersion);
             dest.writeStringList(libraryNames);
diff --git a/core/java/android/content/pm/PackageParserCacheHelper.java b/core/java/android/content/pm/PackageParserCacheHelper.java
new file mode 100644
index 0000000..44def33
--- /dev/null
+++ b/core/java/android/content/pm/PackageParserCacheHelper.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2017 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.content.pm;
+
+import android.os.Parcel;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * Helper classes to read from and write to Parcel with pooled strings.
+ *
+ * @hide
+ */
+public class PackageParserCacheHelper {
+    private PackageParserCacheHelper() {
+    }
+
+    private static final String TAG = "PackageParserCacheHelper";
+    private static final boolean DEBUG = false;
+
+    /**
+     * Parcel read helper with a string pool.
+     */
+    public static class ReadHelper extends Parcel.ReadWriteHelper {
+        private final ArrayList<String> mStrings = new ArrayList<>();
+
+        private final Parcel mParcel;
+
+        public ReadHelper(Parcel p) {
+            mParcel = p;
+        }
+
+        /**
+         * Prepare to read from a parcel, and install itself as a read-write helper.
+         *
+         * (We don't do it in the constructor to avoid calling methods before the constructor
+         * finishes.)
+         */
+        public void startAndInstall() {
+            mStrings.clear();
+
+            final int poolPosition = mParcel.readInt();
+            final int startPosition = mParcel.dataPosition();
+
+            // The pool is at the end of the parcel.
+            mParcel.setDataPosition(poolPosition);
+            mParcel.readStringList(mStrings);
+
+            // Then move back.
+            mParcel.setDataPosition(startPosition);
+
+            if (DEBUG) {
+                Log.i(TAG, "Read " + mStrings.size() + " strings");
+                for (int i = 0; i < mStrings.size(); i++) {
+                    Log.i(TAG, "  " + i + ": \"" + mStrings.get(i) + "\"");
+                }
+            }
+
+            mParcel.setReadWriteHelper(this);
+        }
+
+        /**
+         * Read an string index from a parcel, and returns the corresponding string from the pool.
+         */
+        @Override
+        public String readString(Parcel p) {
+            return mStrings.get(p.readInt());
+        }
+    }
+
+    /**
+     * Parcel write helper with a string pool.
+     */
+    public static class WriteHelper extends Parcel.ReadWriteHelper {
+        private final ArrayList<String> mStrings = new ArrayList<>();
+
+        private final HashMap<String, Integer> mIndexes = new HashMap<>();
+
+        private final Parcel mParcel;
+        private final int mStartPos;
+
+        /**
+         * Constructor.  Prepare a parcel, and install it self as a read-write helper.
+         */
+        public WriteHelper(Parcel p) {
+            mParcel = p;
+            mStartPos = p.dataPosition();
+            mParcel.writeInt(0); // We come back later here and write the pool position.
+
+            mParcel.setReadWriteHelper(this);
+        }
+
+        /**
+         * Instead of writing a string directly to a parcel, this method adds it to the pool,
+         * and write the index in the pool to the parcel.
+         */
+        @Override
+        public void writeString(Parcel p, String s) {
+            final Integer cur = mIndexes.get(s);
+            if (cur != null) {
+                // String already in the pool. Just write the index.
+                p.writeInt(cur); // Already in the pool.
+                if (DEBUG) {
+                    Log.i(TAG, "Duplicate '" + s + "' at " + cur);
+                }
+            } else {
+                // Not in the pool. Add to the pool, and write the index.
+                final int index = mStrings.size();
+                mIndexes.put(s, index);
+                mStrings.add(s);
+
+                if (DEBUG) {
+                    Log.i(TAG, "New '" + s + "' at " + index);
+                }
+
+                p.writeInt(index);
+            }
+        }
+
+        /**
+         * Closes a parcel by appending the string pool at the end and updating the pool offset,
+         * which it assumes is at the first byte.  It also uninstalls itself as a read-write helper.
+         */
+        public void finishAndUninstall() {
+            // Uninstall first, so that writeStringList() uses the native writeString.
+            mParcel.setReadWriteHelper(null);
+
+            final int poolPosition = mParcel.dataPosition();
+            mParcel.writeStringList(mStrings);
+
+            mParcel.setDataPosition(mStartPos);
+            mParcel.writeInt(poolPosition);
+
+            // Move back to the end.
+            mParcel.setDataPosition(mParcel.dataSize());
+            if (DEBUG) {
+                Log.i(TAG, "Wrote " + mStrings.size() + " strings");
+            }
+        }
+    }
+}
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 5b6efd4..de02ee5 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -703,12 +703,19 @@
     /**
      * Open the database according to the specified {@link OpenParams parameters}
      *
-     * @param path to database file to open and/or create
+     * @param path path to database file to open and/or create.
+     * <p><strong>Important:</strong> The file should be constructed either from an absolute path or
+     * by using {@link android.content.Context#getDatabasePath(String)}.
      * @param openParams configuration parameters that are used for opening {@link SQLiteDatabase}
      * @return the newly opened database
      * @throws SQLiteException if the database cannot be opened
      */
-    public static SQLiteDatabase openDatabase(@NonNull String path,
+    public static SQLiteDatabase openDatabase(@NonNull File path,
+            @NonNull OpenParams openParams) {
+        return openDatabase(path.getPath(), openParams);
+    }
+
+    private static SQLiteDatabase openDatabase(@NonNull String path,
             @NonNull OpenParams openParams) {
         Preconditions.checkArgument(openParams != null, "OpenParams cannot be null");
         SQLiteDatabase db = new SQLiteDatabase(path, openParams.mOpenFlags,
@@ -873,7 +880,8 @@
      *
      * @param factory an optional factory class that is called to instantiate a
      *            cursor when query is called
-     * @return a SQLiteDatabase object, or null if the database can't be created
+     * @return a SQLiteDatabase instance
+     * @throws SQLiteException if the database cannot be created
      */
     @NonNull
     public static SQLiteDatabase create(@Nullable CursorFactory factory) {
@@ -889,7 +897,8 @@
      * <p>Sets the locale of the database to the  the system's current locale.
      * Call {@link #setLocale} if you would like something else.</p>
      * @param openParams configuration parameters that are used for opening SQLiteDatabase
-     * @return a SQLiteDatabase object, or null if the database can't be created
+     * @return a SQLiteDatabase instance
+     * @throws SQLException if the database cannot be created
      */
     @NonNull
     public static SQLiteDatabase createInMemory(@NonNull OpenParams openParams) {
@@ -2322,7 +2331,7 @@
         }
 
         /**
-         * Returns flags to control database access mode
+         * Returns flags to control database access mode. Default value is 0.
          *
          * @see Builder#setOpenFlags(int)
          */
diff --git a/core/java/android/database/sqlite/SQLiteOpenHelper.java b/core/java/android/database/sqlite/SQLiteOpenHelper.java
index dfaf714..cc9e0f4 100644
--- a/core/java/android/database/sqlite/SQLiteOpenHelper.java
+++ b/core/java/android/database/sqlite/SQLiteOpenHelper.java
@@ -289,12 +289,12 @@
             } else if (mName == null) {
                 db = SQLiteDatabase.createInMemory(mOpenParamsBuilder.build());
             } else {
-                final String path = mContext.getDatabasePath(mName).getPath();
+                final File filePath = mContext.getDatabasePath(mName);
                 SQLiteDatabase.OpenParams params = mOpenParamsBuilder.build();
                 try {
-                    db = SQLiteDatabase.openDatabase(path, params);
+                    db = SQLiteDatabase.openDatabase(filePath, params);
                     // Keep pre-O-MR1 behavior by resetting file permissions to 660
-                    setFilePermissionsForDb(path);
+                    setFilePermissionsForDb(filePath.getPath());
                 } catch (SQLException ex) {
                     if (writable) {
                         throw ex;
@@ -302,7 +302,7 @@
                     Log.e(TAG, "Couldn't open " + mName
                             + " for writing (will try read-only):", ex);
                     params = params.toBuilder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
-                    db = SQLiteDatabase.openDatabase(path, params);
+                    db = SQLiteDatabase.openDatabase(filePath, params);
                 }
             }
 
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 63eedf5..55343a2 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -73,8 +73,10 @@
      * Create a request suitable for still image capture. Specifically, this
      * means prioritizing image quality over frame rate. These requests would
      * commonly be used with the {@link CameraCaptureSession#capture} method.
-     * This template is guaranteed to be supported on all camera devices.
-     *
+     * This template is guaranteed to be supported on all camera devices except
+     * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT DEPTH_OUTPUT} devices
+     * that are not {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
+     * BACKWARD_COMPATIBLE}.
      * @see #createCaptureRequest
      */
     public static final int TEMPLATE_STILL_CAPTURE = 2;
@@ -84,7 +86,10 @@
      * that a stable frame rate is used, and post-processing is set for
      * recording quality. These requests would commonly be used with the
      * {@link CameraCaptureSession#setRepeatingRequest} method.
-     * This template is guaranteed to be supported on all camera devices.
+     * This template is guaranteed to be supported on all camera devices except
+     * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT DEPTH_OUTPUT} devices
+     * that are not {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
+     * BACKWARD_COMPATIBLE}.
      *
      * @see #createCaptureRequest
      */
@@ -98,7 +103,10 @@
      * {@link #TEMPLATE_RECORD} is is in use with {@link CameraCaptureSession#setRepeatingRequest}.
      * This template is guaranteed to be supported on all camera devices except
      * legacy devices ({@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}
-     * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY})
+     * {@code == }{@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY LEGACY}) and
+     * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT DEPTH_OUTPUT} devices
+     * that are not {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE
+     * BACKWARD_COMPATIBLE}.
      *
      * @see #createCaptureRequest
      */
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index a5bf639..233c4d4 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -99,6 +99,8 @@
     /**
      * The {@link FingerprintManager#remove} call failed. Typically this will happen when the
      * provided fingerprint id was incorrect.
+     *
+     * @hide
      */
     public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;
 
diff --git a/core/java/android/hardware/radio/ITuner.aidl b/core/java/android/hardware/radio/ITuner.aidl
index 7f470ef..3aaeb50 100644
--- a/core/java/android/hardware/radio/ITuner.aidl
+++ b/core/java/android/hardware/radio/ITuner.aidl
@@ -68,18 +68,19 @@
     Bitmap getImage(int id);
 
     /**
-     * @returns {@code true} if the scan was properly scheduled,
+     * @return {@code true} if the scan was properly scheduled,
      *          {@code false} if the scan feature is unavailable
      */
     boolean startBackgroundScan();
 
     /**
-     * @returns the list, or null if scan is in progress
+     * @param vendorFilter Vendor-specific filter, must be Map<String, String>
+     * @return the list, or null if scan is in progress
      * @throws IllegalArgumentException if invalid arguments are passed
      * @throws IllegalStateException if the scan has not been started, client may
      *         call startBackgroundScan to fix this.
      */
-    List<RadioManager.ProgramInfo> getProgramList(String filter);
+    List<RadioManager.ProgramInfo> getProgramList(in Map vendorFilter);
 
     /**
      * @throws IllegalStateException if the switch is not supported at current
diff --git a/core/java/android/hardware/radio/ITunerCallback.aidl b/core/java/android/hardware/radio/ITunerCallback.aidl
index c3bbaec..6ed171b 100644
--- a/core/java/android/hardware/radio/ITunerCallback.aidl
+++ b/core/java/android/hardware/radio/ITunerCallback.aidl
@@ -23,7 +23,7 @@
 oneway interface ITunerCallback {
     void onError(int status);
     void onConfigurationChanged(in RadioManager.BandConfig config);
-    void onProgramInfoChanged();
+    void onCurrentProgramInfoChanged(in RadioManager.ProgramInfo info);
     void onTrafficAnnouncement(boolean active);
     void onEmergencyAnnouncement(boolean active);
     void onAntennaState(boolean connected);
diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java
index d9e306f..2211cee 100644
--- a/core/java/android/hardware/radio/ProgramSelector.java
+++ b/core/java/android/hardware/radio/ProgramSelector.java
@@ -17,6 +17,7 @@
 package android.hardware.radio;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -73,10 +74,8 @@
     /** SiriusXM Satellite Radio. */
     public static final int PROGRAM_TYPE_SXM = 7;
     /** Vendor-specific, not synced across devices. */
-    public static final int PROGRAM_TYPE_VENDOR1 = 8;
-    public static final int PROGRAM_TYPE_VENDOR2 = 9;
-    public static final int PROGRAM_TYPE_VENDOR3 = 10;
-    public static final int PROGRAM_TYPE_VENDOR4 = 11;
+    public static final int PROGRAM_TYPE_VENDOR_START = 1000;
+    public static final int PROGRAM_TYPE_VENDOR_END = 1999;
     @IntDef(prefix = { "PROGRAM_TYPE_" }, value = {
         PROGRAM_TYPE_AM,
         PROGRAM_TYPE_FM,
@@ -85,11 +84,8 @@
         PROGRAM_TYPE_DAB,
         PROGRAM_TYPE_DRMO,
         PROGRAM_TYPE_SXM,
-        PROGRAM_TYPE_VENDOR1,
-        PROGRAM_TYPE_VENDOR2,
-        PROGRAM_TYPE_VENDOR3,
-        PROGRAM_TYPE_VENDOR4,
     })
+    @IntRange(from = PROGRAM_TYPE_VENDOR_START, to = PROGRAM_TYPE_VENDOR_END)
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProgramType {}
 
@@ -145,12 +141,12 @@
      * Primary identifier for vendor-specific radio technology.
      * The value format is determined by a vendor.
      *
-     * It must not be used in any other programType than VENDORx.
+     * It must not be used in any other programType than corresponding VENDOR
+     * type between VENDOR_START and VENDOR_END (eg. identifier type 1015 must
+     * not be used in any program type other than 1015).
      */
-    public static final int IDENTIFIER_TYPE_VENDOR1_PRIMARY = 14;
-    public static final int IDENTIFIER_TYPE_VENDOR2_PRIMARY = 15;
-    public static final int IDENTIFIER_TYPE_VENDOR3_PRIMARY = 16;
-    public static final int IDENTIFIER_TYPE_VENDOR4_PRIMARY = 17;
+    public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = PROGRAM_TYPE_VENDOR_START;
+    public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = PROGRAM_TYPE_VENDOR_END;
     @IntDef(prefix = { "IDENTIFIER_TYPE_" }, value = {
         IDENTIFIER_TYPE_AMFM_FREQUENCY,
         IDENTIFIER_TYPE_RDS_PI,
@@ -165,11 +161,8 @@
         IDENTIFIER_TYPE_DRMO_MODULATION,
         IDENTIFIER_TYPE_SXM_SERVICE_ID,
         IDENTIFIER_TYPE_SXM_CHANNEL,
-        IDENTIFIER_TYPE_VENDOR1_PRIMARY,
-        IDENTIFIER_TYPE_VENDOR2_PRIMARY,
-        IDENTIFIER_TYPE_VENDOR3_PRIMARY,
-        IDENTIFIER_TYPE_VENDOR4_PRIMARY,
     })
+    @IntRange(from = IDENTIFIER_TYPE_VENDOR_PRIMARY_START, to = IDENTIFIER_TYPE_VENDOR_PRIMARY_END)
     @Retention(RetentionPolicy.SOURCE)
     public @interface IdentifierType {}
 
@@ -207,7 +200,7 @@
     /**
      * Type of a radio technology.
      *
-     * @returns program type.
+     * @return program type.
      */
     public @ProgramType int getProgramType() {
         return mProgramType;
@@ -217,7 +210,7 @@
      * Primary program identifier uniquely identifies a station and is used to
      * determine equality between two ProgramSelectors.
      *
-     * @returns primary identifier.
+     * @return primary identifier.
      */
     public @NonNull Identifier getPrimaryId() {
         return mPrimaryId;
@@ -227,7 +220,7 @@
      * Secondary program identifier is not required for tuning, but may make it
      * faster or more reliable.
      *
-     * @returns secondary identifier list, must not be modified.
+     * @return secondary identifier list, must not be modified.
      */
     public @NonNull Identifier[] getSecondaryIds() {
         return mSecondaryIds;
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index fcb3d86..4f4361f 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -35,7 +35,9 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
 
@@ -117,6 +119,25 @@
      * @see BandDescriptor */
     public static final int REGION_KOREA  = 4;
 
+    private static void writeStringMap(@NonNull Parcel dest, @NonNull Map<String, String> map) {
+        dest.writeInt(map.size());
+        for (Map.Entry<String, String> entry : map.entrySet()) {
+            dest.writeString(entry.getKey());
+            dest.writeString(entry.getValue());
+        }
+    }
+
+    private static @NonNull Map<String, String> readStringMap(@NonNull Parcel in) {
+        int size = in.readInt();
+        Map<String, String> map = new HashMap<>();
+        while (size-- > 0) {
+            String key = in.readString();
+            String value = in.readString();
+            map.put(key, value);
+        }
+        return map;
+    }
+
     /*****************************************************************************
      * Lists properties, options and radio bands supported by a given broadcast radio module.
      * Each module has a unique ID used to address it when calling RadioManager APIs.
@@ -138,14 +159,14 @@
         private final boolean mIsBgScanSupported;
         private final Set<Integer> mSupportedProgramTypes;
         private final Set<Integer> mSupportedIdentifierTypes;
-        private final String mVendorInfo;
+        @NonNull private final Map<String, String> mVendorInfo;
 
         ModuleProperties(int id, String serviceName, int classId, String implementor,
                 String product, String version, String serial, int numTuners, int numAudioSources,
                 boolean isCaptureSupported, BandDescriptor[] bands, boolean isBgScanSupported,
                 @ProgramSelector.ProgramType int[] supportedProgramTypes,
                 @ProgramSelector.IdentifierType int[] supportedIdentifierTypes,
-                String vendorInfo) {
+                Map<String, String> vendorInfo) {
             mId = id;
             mServiceName = TextUtils.isEmpty(serviceName) ? "default" : serviceName;
             mClassId = classId;
@@ -160,7 +181,7 @@
             mIsBgScanSupported = isBgScanSupported;
             mSupportedProgramTypes = arrayToSet(supportedProgramTypes);
             mSupportedIdentifierTypes = arrayToSet(supportedIdentifierTypes);
-            mVendorInfo = vendorInfo;
+            mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo;
         }
 
         private static Set<Integer> arrayToSet(int[] arr) {
@@ -287,17 +308,17 @@
         }
 
         /**
-         * Opaque vendor-specific string, passed from HAL without changes.
-         * Format of this string can vary across vendors.
+         * A map of vendor-specific opaque strings, passed from HAL without changes.
+         * Format of these strings can vary across vendors.
          *
          * It may be used for extra features, that's not supported by a platform,
-         * for example: "preset-slots=6;ultra-hd-capable=false".
+         * for example: preset-slots=6; ultra-hd-capable=false.
          *
-         * Client application MUST verify vendor/product name from the
-         * ModuleProperties class before doing any interpretation of this value.
+         * Keys must be prefixed with unique vendor Java-style namespace,
+         * eg. 'com.somecompany.parameter1'.
          */
-        public @NonNull String getVendorInfo() {
-            return mVendorInfo == null ? "" : mVendorInfo;
+        public @NonNull Map<String, String> getVendorInfo() {
+            return mVendorInfo;
         }
 
         /** List of descriptors for all bands supported by this module.
@@ -327,7 +348,7 @@
             mIsBgScanSupported = in.readInt() == 1;
             mSupportedProgramTypes = arrayToSet(in.createIntArray());
             mSupportedIdentifierTypes = arrayToSet(in.createIntArray());
-            mVendorInfo = in.readString();
+            mVendorInfo = readStringMap(in);
         }
 
         public static final Parcelable.Creator<ModuleProperties> CREATOR
@@ -357,7 +378,7 @@
             dest.writeInt(mIsBgScanSupported ? 1 : 0);
             dest.writeIntArray(setToArray(mSupportedProgramTypes));
             dest.writeIntArray(setToArray(mSupportedIdentifierTypes));
-            dest.writeString(mVendorInfo);
+            writeStringMap(dest, mVendorInfo);
         }
 
         @Override
@@ -394,7 +415,7 @@
             result = prime * result + (mIsCaptureSupported ? 1 : 0);
             result = prime * result + Arrays.hashCode(mBands);
             result = prime * result + (mIsBgScanSupported ? 1 : 0);
-            result = prime * result + ((mVendorInfo == null) ? 0 : mVendorInfo.hashCode());
+            result = prime * result + mVendorInfo.hashCode();
             return result;
         }
 
@@ -440,7 +461,7 @@
                 return false;
             if (mIsBgScanSupported != other.isBackgroundScanningSupported())
                 return false;
-            if (!TextUtils.equals(mVendorInfo, other.mVendorInfo)) return false;
+            if (!mVendorInfo.equals(other.mVendorInfo)) return false;
             return true;
         }
     }
@@ -1324,11 +1345,11 @@
         private final int mFlags;
         private final int mSignalStrength;
         private final RadioMetadata mMetadata;
-        private final String mVendorInfo;
+        @NonNull private final Map<String, String> mVendorInfo;
 
         ProgramInfo(@NonNull ProgramSelector selector, boolean tuned, boolean stereo,
                 boolean digital, int signalStrength, RadioMetadata metadata, int flags,
-                String vendorInfo) {
+                Map<String, String> vendorInfo) {
             mSelector = selector;
             mTuned = tuned;
             mStereo = stereo;
@@ -1336,7 +1357,7 @@
             mFlags = flags;
             mSignalStrength = signalStrength;
             mMetadata = metadata;
-            mVendorInfo = vendorInfo;
+            mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo;
         }
 
         /**
@@ -1447,17 +1468,17 @@
         }
 
         /**
-         * Opaque vendor-specific string, passed from HAL without changes.
-         * Format of this string can vary across vendors.
+         * A map of vendor-specific opaque strings, passed from HAL without changes.
+         * Format of these strings can vary across vendors.
          *
          * It may be used for extra features, that's not supported by a platform,
-         * for example: "paid-service=true;bitrate=320kbps".
+         * for example: paid-service=true; bitrate=320kbps.
          *
-         * Client application MUST verify vendor/product name from the
-         * ModuleProperties class before doing any interpretation of this value.
+         * Keys must be prefixed with unique vendor Java-style namespace,
+         * eg. 'com.somecompany.parameter1'.
          */
-        public @NonNull String getVendorInfo() {
-            return mVendorInfo == null ? "" : mVendorInfo;
+        public @NonNull Map<String, String> getVendorInfo() {
+            return mVendorInfo;
         }
 
         private ProgramInfo(Parcel in) {
@@ -1472,7 +1493,7 @@
                 mMetadata = null;
             }
             mFlags = in.readInt();
-            mVendorInfo = in.readString();
+            mVendorInfo = readStringMap(in);
         }
 
         public static final Parcelable.Creator<ProgramInfo> CREATOR
@@ -1500,7 +1521,7 @@
                 mMetadata.writeToParcel(dest, flags);
             }
             dest.writeInt(mFlags);
-            dest.writeString(mVendorInfo);
+            writeStringMap(dest, mVendorInfo);
         }
 
         @Override
@@ -1528,7 +1549,7 @@
             result = prime * result + mFlags;
             result = prime * result + mSignalStrength;
             result = prime * result + ((mMetadata == null) ? 0 : mMetadata.hashCode());
-            result = prime * result + ((mVendorInfo == null) ? 0 : mVendorInfo.hashCode());
+            result = prime * result + mVendorInfo.hashCode();
             return result;
         }
 
@@ -1555,7 +1576,7 @@
                     return false;
             } else if (!mMetadata.equals(other.getMetadata()))
                 return false;
-            if (!TextUtils.equals(mVendorInfo, other.mVendorInfo)) return false;
+            if (!mVendorInfo.equals(other.mVendorInfo)) return false;
             return true;
         }
     }
@@ -1635,7 +1656,6 @@
             Log.e(TAG, "Failed to open tuner");
             return null;
         }
-        halCallback.attachTuner(tuner);
         return new TunerAdapter(tuner, config != null ? config.getType() : BAND_INVALID);
     }
 
diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java
index be37cd0..6e8991a 100644
--- a/core/java/android/hardware/radio/RadioTuner.java
+++ b/core/java/android/hardware/radio/RadioTuner.java
@@ -23,6 +23,7 @@
 import android.os.Handler;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * RadioTuner interface provides methods to control a radio tuner on the device: selecting and
@@ -182,7 +183,7 @@
      * The operation is asynchronous and {@link Callback} onProgramInfoChanged() will be called
      * when tune completes or onError() when cancelled or on timeout.
      *
-     * @thows IllegalArgumentException if the provided selector is invalid
+     * @throws IllegalArgumentException if the provided selector is invalid
      */
     public abstract void tune(@NonNull ProgramSelector selector);
 
@@ -270,16 +271,18 @@
     /**
      * Get the list of discovered radio stations.
      *
-     * To get the full list, set filter to null or empty string. Otherwise, client application
-     * must verify vendor product/name before setting this parameter to anything else.
+     * To get the full list, set filter to null or empty map.
+     * Keys must be prefixed with unique vendor Java-style namespace,
+     * eg. 'com.somecompany.parameter1'.
      *
-     * @param filter vendor-specific selector for radio stations.
+     * @param vendorFilter vendor-specific selector for radio stations.
      * @return a list of radio stations.
      * @throws IllegalStateException if the scan is in progress or has not been started,
      *         startBackgroundScan() call may fix it.
-     * @throws IllegalArgumentException if the filter argument is not valid.
+     * @throws IllegalArgumentException if the vendorFilter argument is not valid.
      */
-    public abstract @NonNull List<RadioManager.ProgramInfo> getProgramList(@Nullable String filter);
+    public abstract @NonNull List<RadioManager.ProgramInfo>
+            getProgramList(@Nullable Map<String, String> vendorFilter);
 
     /**
      * Checks, if the analog playback is forced, see setAnalogForced.
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index c687537..b621969 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -23,6 +23,7 @@
 import android.util.Log;
 
 import java.util.List;
+import java.util.Map;
 
 /**
  * Implements the RadioTuner interface by forwarding calls to radio service.
@@ -222,9 +223,10 @@
     }
 
     @Override
-    public @NonNull List<RadioManager.ProgramInfo> getProgramList(@Nullable String filter) {
+    public @NonNull List<RadioManager.ProgramInfo>
+            getProgramList(@Nullable Map<String, String> vendorFilter) {
         try {
-            return mTuner.getProgramList(filter);
+            return mTuner.getProgramList(vendorFilter);
         } catch (RemoteException e) {
             throw new RuntimeException("service died", e);
         }
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index 00a36c8..ffd5b30f 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -20,7 +20,6 @@
 import android.annotation.Nullable;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.RemoteException;
 import android.util.Log;
 
 /**
@@ -31,10 +30,6 @@
 
     @NonNull private final RadioTuner.Callback mCallback;
     @NonNull private final Handler mHandler;
-    private final Object mLock = new Object();
-
-    @Nullable private ITuner mTuner;
-    boolean mPendingProgramInfoChanged = false;
 
     TunerCallbackAdapter(@NonNull RadioTuner.Callback callback, @Nullable Handler handler) {
         mCallback = callback;
@@ -45,14 +40,6 @@
         }
     }
 
-    public void attachTuner(@NonNull ITuner tuner) {
-        synchronized (mLock) {
-            if (mTuner != null) throw new IllegalStateException();
-            mTuner = tuner;
-            if (mPendingProgramInfoChanged) onProgramInfoChanged();
-        }
-    }
-
     @Override
     public void onError(int status) {
         mHandler.post(() -> mCallback.onError(status));
@@ -64,19 +51,9 @@
     }
 
     @Override
-    public void onProgramInfoChanged() {
-        synchronized (mLock) {
-            if (mTuner == null) {
-                mPendingProgramInfoChanged = true;
-                return;
-            }
-        }
-
-        RadioManager.ProgramInfo info;
-        try {
-            info = mTuner.getProgramInformation();
-        } catch (RemoteException e) {
-            Log.e(TAG, "service died", e);
+    public void onCurrentProgramInfoChanged(RadioManager.ProgramInfo info) {
+        if (info == null) {
+            Log.e(TAG, "ProgramInfo must not be null");
             return;
         }
 
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index ed223d1..d2e3510 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -16,10 +16,6 @@
 
 package android.inputmethodservice;
 
-import com.android.internal.os.HandlerCaller;
-import com.android.internal.os.SomeArgs;
-import com.android.internal.view.IInputMethodSession;
-
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -34,9 +30,13 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CursorAnchorInfo;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.InputMethodSession;
-import android.view.inputmethod.CursorAnchorInfo;
+
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.view.IInputMethodSession;
 
 class IInputMethodSessionWrapper extends IInputMethodSession.Stub
         implements HandlerCaller.Callback {
@@ -218,7 +218,7 @@
         }
 
         @Override
-        public void onInputEvent(InputEvent event) {
+        public void onInputEvent(InputEvent event, int displayId) {
             if (mInputMethodSession == null) {
                 // The session has been finished.
                 finishInputEvent(event, false);
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index d2af023..0b92893 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -773,7 +773,7 @@
         // TODO: consider only enforcing that capabilities are not removed, allowing addition.
         // Ignore NOT_METERED being added or removed as it is effectively dynamic. http://b/63326103
         // TODO: properly support NOT_METERED as a mutable and requestable capability.
-        final long mask = ~MUTABLE_CAPABILITIES & ~NET_CAPABILITY_NOT_METERED;
+        final long mask = ~MUTABLE_CAPABILITIES & ~(1 << NET_CAPABILITY_NOT_METERED);
         long oldImmutableCapabilities = this.mNetworkCapabilities & mask;
         long newImmutableCapabilities = that.mNetworkCapabilities & mask;
         if (oldImmutableCapabilities != newImmutableCapabilities) {
diff --git a/core/java/android/net/NetworkRecommendationProvider.java b/core/java/android/net/NetworkRecommendationProvider.java
index fdb4ba0..a70c97b 100644
--- a/core/java/android/net/NetworkRecommendationProvider.java
+++ b/core/java/android/net/NetworkRecommendationProvider.java
@@ -35,6 +35,7 @@
  * A network recommendation provider is any application which:
  * <ul>
  * <li>Is granted the {@link permission#SCORE_NETWORKS} permission.
+ * <li>Is granted the {@link permission#ACCESS_COARSE_LOCATION} permission.
  * <li>Includes a Service for the {@link NetworkScoreManager#ACTION_RECOMMEND_NETWORKS} intent
  *     which is protected by the {@link permission#BIND_NETWORK_RECOMMENDATION_SERVICE} permission.
  * </ul>
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 7e0c9ce..060af0d 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -38,7 +38,8 @@
  *
  * <p>A network scorer is any application which:
  * <ul>
- * <li>Declares the {@link permission#SCORE_NETWORKS} permission.
+ * <li>Is granted the {@link permission#SCORE_NETWORKS} permission.
+ * <li>Is granted the {@link permission#ACCESS_COARSE_LOCATION} permission.
  * <li>Include a Service for the {@link #ACTION_RECOMMEND_NETWORKS} action
  *     protected by the {@link permission#BIND_NETWORK_RECOMMENDATION_SERVICE}
  *     permission.
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 77ce65b..be9e809 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -672,36 +672,33 @@
             entry.tag = left.tag[i];
             entry.metered = left.metered[i];
             entry.roaming = left.roaming[i];
+            entry.rxBytes = left.rxBytes[i];
+            entry.rxPackets = left.rxPackets[i];
+            entry.txBytes = left.txBytes[i];
+            entry.txPackets = left.txPackets[i];
+            entry.operations = left.operations[i];
 
             // find remote row that matches, and subtract
             final int j = right.findIndexHinted(entry.iface, entry.uid, entry.set, entry.tag,
                     entry.metered, entry.roaming, i);
-            if (j == -1) {
-                // newly appearing row, return entire value
-                entry.rxBytes = left.rxBytes[i];
-                entry.rxPackets = left.rxPackets[i];
-                entry.txBytes = left.txBytes[i];
-                entry.txPackets = left.txPackets[i];
-                entry.operations = left.operations[i];
-            } else {
-                // existing row, subtract remote value
-                entry.rxBytes = left.rxBytes[i] - right.rxBytes[j];
-                entry.rxPackets = left.rxPackets[i] - right.rxPackets[j];
-                entry.txBytes = left.txBytes[i] - right.txBytes[j];
-                entry.txPackets = left.txPackets[i] - right.txPackets[j];
-                entry.operations = left.operations[i] - right.operations[j];
+            if (j != -1) {
+                // Found matching row, subtract remote value.
+                entry.rxBytes -= right.rxBytes[j];
+                entry.rxPackets -= right.rxPackets[j];
+                entry.txBytes -= right.txBytes[j];
+                entry.txPackets -= right.txPackets[j];
+                entry.operations -= right.operations[j];
+            }
 
-                if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
-                        || entry.txPackets < 0 || entry.operations < 0) {
-                    if (observer != null) {
-                        observer.foundNonMonotonic(left, i, right, j, cookie);
-                    }
-                    entry.rxBytes = Math.max(entry.rxBytes, 0);
-                    entry.rxPackets = Math.max(entry.rxPackets, 0);
-                    entry.txBytes = Math.max(entry.txBytes, 0);
-                    entry.txPackets = Math.max(entry.txPackets, 0);
-                    entry.operations = Math.max(entry.operations, 0);
+            if (entry.isNegative()) {
+                if (observer != null) {
+                    observer.foundNonMonotonic(left, i, right, j, cookie);
                 }
+                entry.rxBytes = Math.max(entry.rxBytes, 0);
+                entry.rxPackets = Math.max(entry.rxPackets, 0);
+                entry.txBytes = Math.max(entry.txBytes, 0);
+                entry.txPackets = Math.max(entry.txPackets, 0);
+                entry.operations = Math.max(entry.operations, 0);
             }
 
             result.addValues(entry);
diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java
index ace3748..1e41eea 100644
--- a/core/java/android/net/nsd/NsdManager.java
+++ b/core/java/android/net/nsd/NsdManager.java
@@ -16,6 +16,10 @@
 
 package android.net.nsd;
 
+import static com.android.internal.util.Preconditions.checkArgument;
+import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.Preconditions.checkStringNotEmpty;
+
 import android.annotation.SdkConstant;
 import android.annotation.SystemService;
 import android.annotation.SdkConstant.SdkConstantType;
@@ -240,12 +244,12 @@
         return name;
     }
 
+    private static int FIRST_LISTENER_KEY = 1;
+
     private final INsdManager mService;
     private final Context mContext;
 
-    private static final int INVALID_LISTENER_KEY = 0;
-    private static final int BUSY_LISTENER_KEY = -1;
-    private int mListenerKey = 1;
+    private int mListenerKey = FIRST_LISTENER_KEY;
     private final SparseArray mListenerMap = new SparseArray();
     private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>();
     private final Object mMapLock = new Object();
@@ -311,7 +315,6 @@
         public void onServiceFound(NsdServiceInfo serviceInfo);
 
         public void onServiceLost(NsdServiceInfo serviceInfo);
-
     }
 
     /** Interface for callback invocation for service registration */
@@ -342,8 +345,9 @@
 
         @Override
         public void handleMessage(Message message) {
-            if (DBG) Log.d(TAG, "received " + nameOf(message.what));
-            switch (message.what) {
+            final int what = message.what;
+            final int key = message.arg2;
+            switch (what) {
                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
                     mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
                     return;
@@ -356,19 +360,26 @@
                 default:
                     break;
             }
-            Object listener = getListener(message.arg2);
+            final Object listener;
+            final NsdServiceInfo ns;
+            synchronized (mMapLock) {
+                listener = mListenerMap.get(key);
+                ns = mServiceMap.get(key);
+            }
             if (listener == null) {
                 Log.d(TAG, "Stale key " + message.arg2);
                 return;
             }
-            NsdServiceInfo ns = getNsdService(message.arg2);
-            switch (message.what) {
+            if (DBG) {
+                Log.d(TAG, "received " + nameOf(what) + " for key " + key + ", service " + ns);
+            }
+            switch (what) {
                 case DISCOVER_SERVICES_STARTED:
                     String s = getNsdServiceInfoType((NsdServiceInfo) message.obj);
                     ((DiscoveryListener) listener).onDiscoveryStarted(s);
                     break;
                 case DISCOVER_SERVICES_FAILED:
-                    removeListener(message.arg2);
+                    removeListener(key);
                     ((DiscoveryListener) listener).onStartDiscoveryFailed(getNsdServiceInfoType(ns),
                             message.arg1);
                     break;
@@ -381,16 +392,16 @@
                 case STOP_DISCOVERY_FAILED:
                     // TODO: failure to stop discovery should be internal and retried internally, as
                     // the effect for the client is indistinguishable from STOP_DISCOVERY_SUCCEEDED
-                    removeListener(message.arg2);
+                    removeListener(key);
                     ((DiscoveryListener) listener).onStopDiscoveryFailed(getNsdServiceInfoType(ns),
                             message.arg1);
                     break;
                 case STOP_DISCOVERY_SUCCEEDED:
-                    removeListener(message.arg2);
+                    removeListener(key);
                     ((DiscoveryListener) listener).onDiscoveryStopped(getNsdServiceInfoType(ns));
                     break;
                 case REGISTER_SERVICE_FAILED:
-                    removeListener(message.arg2);
+                    removeListener(key);
                     ((RegistrationListener) listener).onRegistrationFailed(ns, message.arg1);
                     break;
                 case REGISTER_SERVICE_SUCCEEDED:
@@ -398,7 +409,7 @@
                             (NsdServiceInfo) message.obj);
                     break;
                 case UNREGISTER_SERVICE_FAILED:
-                    removeListener(message.arg2);
+                    removeListener(key);
                     ((RegistrationListener) listener).onUnregistrationFailed(ns, message.arg1);
                     break;
                 case UNREGISTER_SERVICE_SUCCEEDED:
@@ -408,11 +419,11 @@
                     ((RegistrationListener) listener).onServiceUnregistered(ns);
                     break;
                 case RESOLVE_SERVICE_FAILED:
-                    removeListener(message.arg2);
+                    removeListener(key);
                     ((ResolveListener) listener).onResolveFailed(ns, message.arg1);
                     break;
                 case RESOLVE_SERVICE_SUCCEEDED:
-                    removeListener(message.arg2);
+                    removeListener(key);
                     ((ResolveListener) listener).onServiceResolved((NsdServiceInfo) message.obj);
                     break;
                 default:
@@ -422,40 +433,27 @@
         }
     }
 
-    // if the listener is already in the map, reject it.  Otherwise, add it and
-    // return its key.
+    private int nextListenerKey() {
+        // Ensure mListenerKey >= FIRST_LISTENER_KEY;
+        mListenerKey = Math.max(FIRST_LISTENER_KEY, mListenerKey + 1);
+        return mListenerKey;
+    }
+
+    // Assert that the listener is not in the map, then add it and returns its key
     private int putListener(Object listener, NsdServiceInfo s) {
-        if (listener == null) return INVALID_LISTENER_KEY;
-        int key;
+        checkListener(listener);
+        final int key;
         synchronized (mMapLock) {
             int valueIndex = mListenerMap.indexOfValue(listener);
-            if (valueIndex != -1) {
-                return BUSY_LISTENER_KEY;
-            }
-            do {
-                key = mListenerKey++;
-            } while (key == INVALID_LISTENER_KEY);
+            checkArgument(valueIndex == -1, "listener already in use");
+            key = nextListenerKey();
             mListenerMap.put(key, listener);
             mServiceMap.put(key, s);
         }
         return key;
     }
 
-    private Object getListener(int key) {
-        if (key == INVALID_LISTENER_KEY) return null;
-        synchronized (mMapLock) {
-            return mListenerMap.get(key);
-        }
-    }
-
-    private NsdServiceInfo getNsdService(int key) {
-        synchronized (mMapLock) {
-            return mServiceMap.get(key);
-        }
-    }
-
     private void removeListener(int key) {
-        if (key == INVALID_LISTENER_KEY) return;
         synchronized (mMapLock) {
             mListenerMap.remove(key);
             mServiceMap.remove(key);
@@ -463,16 +461,15 @@
     }
 
     private int getListenerKey(Object listener) {
+        checkListener(listener);
         synchronized (mMapLock) {
             int valueIndex = mListenerMap.indexOfValue(listener);
-            if (valueIndex != -1) {
-                return mListenerMap.keyAt(valueIndex);
-            }
+            checkArgument(valueIndex != -1, "listener not registered");
+            return mListenerMap.keyAt(valueIndex);
         }
-        return INVALID_LISTENER_KEY;
     }
 
-    private String getNsdServiceInfoType(NsdServiceInfo s) {
+    private static String getNsdServiceInfoType(NsdServiceInfo s) {
         if (s == null) return "?";
         return s.getServiceType();
     }
@@ -482,7 +479,9 @@
      */
     private void init() {
         final Messenger messenger = getMessenger();
-        if (messenger == null) throw new RuntimeException("Failed to initialize");
+        if (messenger == null) {
+            fatal("Failed to obtain service Messenger");
+        }
         HandlerThread t = new HandlerThread("NsdManager");
         t.start();
         mHandler = new ServiceHandler(t.getLooper());
@@ -490,10 +489,15 @@
         try {
             mConnected.await();
         } catch (InterruptedException e) {
-            Log.e(TAG, "interrupted wait at init");
+            fatal("Interrupted wait at init");
         }
     }
 
+    private static void fatal(String msg) {
+        Log.e(TAG, msg);
+        throw new RuntimeException(msg);
+    }
+
     /**
      * Register a service to be discovered by other services.
      *
@@ -513,23 +517,10 @@
      */
     public void registerService(NsdServiceInfo serviceInfo, int protocolType,
             RegistrationListener listener) {
-        if (TextUtils.isEmpty(serviceInfo.getServiceName()) ||
-                TextUtils.isEmpty(serviceInfo.getServiceType())) {
-            throw new IllegalArgumentException("Service name or type cannot be empty");
-        }
-        if (serviceInfo.getPort() <= 0) {
-            throw new IllegalArgumentException("Invalid port number");
-        }
-        if (listener == null) {
-            throw new IllegalArgumentException("listener cannot be null");
-        }
-        if (protocolType != PROTOCOL_DNS_SD) {
-            throw new IllegalArgumentException("Unsupported protocol");
-        }
+        checkArgument(serviceInfo.getPort() > 0, "Invalid port number");
+        checkServiceInfo(serviceInfo);
+        checkProtocol(protocolType);
         int key = putListener(listener, serviceInfo);
-        if (key == BUSY_LISTENER_KEY) {
-            throw new IllegalArgumentException("listener already in use");
-        }
         mAsyncChannel.sendMessage(REGISTER_SERVICE, 0, key, serviceInfo);
     }
 
@@ -548,12 +539,6 @@
      */
     public void unregisterService(RegistrationListener listener) {
         int id = getListenerKey(listener);
-        if (id == INVALID_LISTENER_KEY) {
-            throw new IllegalArgumentException("listener not registered");
-        }
-        if (listener == null) {
-            throw new IllegalArgumentException("listener cannot be null");
-        }
         mAsyncChannel.sendMessage(UNREGISTER_SERVICE, 0, id);
     }
 
@@ -586,25 +571,13 @@
      * Cannot be null. Cannot be in use for an active service discovery.
      */
     public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) {
-        if (listener == null) {
-            throw new IllegalArgumentException("listener cannot be null");
-        }
-        if (TextUtils.isEmpty(serviceType)) {
-            throw new IllegalArgumentException("Service type cannot be empty");
-        }
-
-        if (protocolType != PROTOCOL_DNS_SD) {
-            throw new IllegalArgumentException("Unsupported protocol");
-        }
+        checkStringNotEmpty(serviceType, "Service type cannot be empty");
+        checkProtocol(protocolType);
 
         NsdServiceInfo s = new NsdServiceInfo();
         s.setServiceType(serviceType);
 
         int key = putListener(listener, s);
-        if (key == BUSY_LISTENER_KEY) {
-            throw new IllegalArgumentException("listener already in use");
-        }
-
         mAsyncChannel.sendMessage(DISCOVER_SERVICES, 0, key, s);
     }
 
@@ -626,12 +599,6 @@
      */
     public void stopServiceDiscovery(DiscoveryListener listener) {
         int id = getListenerKey(listener);
-        if (id == INVALID_LISTENER_KEY) {
-            throw new IllegalArgumentException("service discovery not active on listener");
-        }
-        if (listener == null) {
-            throw new IllegalArgumentException("listener cannot be null");
-        }
         mAsyncChannel.sendMessage(STOP_DISCOVERY, 0, id);
     }
 
@@ -645,19 +612,8 @@
      * Cannot be in use for an active service resolution.
      */
     public void resolveService(NsdServiceInfo serviceInfo, ResolveListener listener) {
-        if (TextUtils.isEmpty(serviceInfo.getServiceName()) ||
-                TextUtils.isEmpty(serviceInfo.getServiceType())) {
-            throw new IllegalArgumentException("Service name or type cannot be empty");
-        }
-        if (listener == null) {
-            throw new IllegalArgumentException("listener cannot be null");
-        }
-
+        checkServiceInfo(serviceInfo);
         int key = putListener(listener, serviceInfo);
-
-        if (key == BUSY_LISTENER_KEY) {
-            throw new IllegalArgumentException("listener already in use");
-        }
         mAsyncChannel.sendMessage(RESOLVE_SERVICE, 0, key, serviceInfo);
     }
 
@@ -671,10 +627,10 @@
     }
 
     /**
-     * Get a reference to NetworkService handler. This is used to establish
+     * Get a reference to NsdService handler. This is used to establish
      * an AsyncChannel communication with the service
      *
-     * @return Messenger pointing to the NetworkService handler
+     * @return Messenger pointing to the NsdService handler
      */
     private Messenger getMessenger() {
         try {
@@ -683,4 +639,18 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    private static void checkListener(Object listener) {
+        checkNotNull(listener, "listener cannot be null");
+    }
+
+    private static void checkProtocol(int protocolType) {
+        checkArgument(protocolType == PROTOCOL_DNS_SD, "Unsupported protocol");
+    }
+
+    private static void checkServiceInfo(NsdServiceInfo serviceInfo) {
+        checkNotNull(serviceInfo, "NsdServiceInfo cannot be null");
+        checkStringNotEmpty(serviceInfo.getServiceName(),"Service name cannot be empty");
+        checkStringNotEmpty(serviceInfo.getServiceType(), "Service type cannot be empty");
+    }
 }
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index a5763ef..5312dca 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -16,6 +16,7 @@
 
 package android.os;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -23,6 +24,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 
 import java.io.Serializable;
@@ -94,7 +96,8 @@
     private ClassLoader mClassLoader;
 
     /** {@hide} */
-    int mFlags;
+    @VisibleForTesting
+    public int mFlags;
 
     /**
      * Constructs a new, empty Bundle that uses a specific ClassLoader for
@@ -218,59 +221,72 @@
      */
     /* package */ void unparcel() {
         synchronized (this) {
-            final Parcel parcelledData = mParcelledData;
-            if (parcelledData == null) {
-                if (DEBUG) Log.d(TAG, "unparcel "
-                        + Integer.toHexString(System.identityHashCode(this))
-                        + ": no parcelled data");
-                return;
-            }
-
-            if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
-                Slog.wtf(TAG, "Attempting to unparcel a Bundle while in transit; this may "
-                        + "clobber all data inside!", new Throwable());
-            }
-
-            if (isEmptyParcel()) {
-                if (DEBUG) Log.d(TAG, "unparcel "
-                        + Integer.toHexString(System.identityHashCode(this)) + ": empty");
-                if (mMap == null) {
-                    mMap = new ArrayMap<>(1);
-                } else {
-                    mMap.erase();
-                }
-                mParcelledData = null;
-                return;
-            }
-
-            int N = parcelledData.readInt();
-            if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
-                    + ": reading " + N + " maps");
-            if (N < 0) {
-                return;
-            }
-            ArrayMap<String, Object> map = mMap;
-            if (map == null) {
-                map = new ArrayMap<>(N);
+            final Parcel source = mParcelledData;
+            if (source != null) {
+                initializeFromParcelLocked(source, /*recycleParcel=*/ true);
             } else {
-                map.erase();
-                map.ensureCapacity(N);
-            }
-            try {
-                parcelledData.readArrayMapInternal(map, N, mClassLoader);
-            } catch (BadParcelableException e) {
-                if (sShouldDefuse) {
-                    Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
-                    map.erase();
-                } else {
-                    throw e;
+                if (DEBUG) {
+                    Log.d(TAG, "unparcel "
+                            + Integer.toHexString(System.identityHashCode(this))
+                            + ": no parcelled data");
                 }
-            } finally {
-                mMap = map;
-                parcelledData.recycle();
-                mParcelledData = null;
             }
-            if (DEBUG) Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+        }
+    }
+
+    private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel) {
+        if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
+            Slog.wtf(TAG, "Attempting to unparcel a Bundle while in transit; this may "
+                    + "clobber all data inside!", new Throwable());
+        }
+
+        if (isEmptyParcel(parcelledData)) {
+            if (DEBUG) {
+                Log.d(TAG, "unparcel "
+                        + Integer.toHexString(System.identityHashCode(this)) + ": empty");
+            }
+            if (mMap == null) {
+                mMap = new ArrayMap<>(1);
+            } else {
+                mMap.erase();
+            }
+            mParcelledData = null;
+            return;
+        }
+
+        final int count = parcelledData.readInt();
+        if (DEBUG) {
+            Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
+                    + ": reading " + count + " maps");
+        }
+        if (count < 0) {
+            return;
+        }
+        ArrayMap<String, Object> map = mMap;
+        if (map == null) {
+            map = new ArrayMap<>(count);
+        } else {
+            map.erase();
+            map.ensureCapacity(count);
+        }
+        try {
+            parcelledData.readArrayMapInternal(map, count, mClassLoader);
+        } catch (BadParcelableException e) {
+            if (sShouldDefuse) {
+                Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
+                map.erase();
+            } else {
+                throw e;
+            }
+        } finally {
+            mMap = map;
+            if (recycleParcel) {
+                recycleParcel(parcelledData);
+            }
+            mParcelledData = null;
+        }
+        if (DEBUG) {
+            Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
                     + " final map: " + mMap);
         }
     }
@@ -286,7 +302,20 @@
      * @hide
      */
     public boolean isEmptyParcel() {
-        return mParcelledData == NoImagePreloadHolder.EMPTY_PARCEL;
+        return isEmptyParcel(mParcelledData);
+    }
+
+    /**
+     * @hide
+     */
+    private static boolean isEmptyParcel(Parcel p) {
+        return p == NoImagePreloadHolder.EMPTY_PARCEL;
+    }
+
+    private static void recycleParcel(Parcel p) {
+        if (p != null && !isEmptyParcel(p)) {
+            p.recycle();
+        }
     }
 
     /** @hide */
@@ -1476,6 +1505,10 @@
      * @param parcel The parcel to copy this bundle to.
      */
     void writeToParcelInner(Parcel parcel, int flags) {
+        // If the parcel has a read-write helper, we can't just copy the blob, so unparcel it first.
+        if (parcel.hasReadWriteHelper()) {
+            unparcel();
+        }
         // Keep implementation in sync with writeToParcel() in
         // frameworks/native/libs/binder/PersistableBundle.cpp.
         final ArrayMap<String, Object> map;
@@ -1544,6 +1577,15 @@
                     + Integer.toHexString(magic));
         }
 
+        if (parcel.hasReadWriteHelper()) {
+            // If the parcel has a read-write helper, then we can't lazily-unparcel it, so just
+            // unparcel right away.
+            synchronized (this) {
+                initializeFromParcelLocked(parcel, /*recycleParcel=*/ false);
+            }
+            return;
+        }
+
         // Advance within this Parcel
         int offset = parcel.dataPosition();
         parcel.setDataPosition(MathUtils.addOrThrow(offset, length));
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index 7d664ec..6d44330 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -206,8 +206,15 @@
      *   - CPU frequency time per uid
      * New in version 22:
      *   - BLE scan result background count, BLE unoptimized scan time
+     *   - Background partial wakelock time & count
+     * New in version 23:
+     *   - Logging smeared power model values
+     * New in version 24:
+     *   - Fixed bugs in background timers and BLE scan time
+     * New in version 25:
+     *   - Package wakeup alarms are now on screen-off timebase
      */
-    static final String CHECKIN_VERSION = "24";
+    static final String CHECKIN_VERSION = "25";
 
     /**
      * Old version, we hit 9 and ran out of room, need to remove.
@@ -3544,6 +3551,12 @@
                     if (name.indexOf(',') >= 0) {
                         name = name.replace(',', '_');
                     }
+                    if (name.indexOf('\n') >= 0) {
+                        name = name.replace('\n', '_');
+                    }
+                    if (name.indexOf('\r') >= 0) {
+                        name = name.replace('\r', '_');
+                    }
                     dumpLine(pw, uid, category, WAKELOCK_DATA, name, sb.toString());
                 }
             }
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index f45efda..0df6361 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -441,6 +441,11 @@
      *
      * <p>If you want to call this, call transact().
      *
+     * <p>Implementations that are returning a result should generally use
+     * {@link Parcel#writeNoException() Parcel.writeNoException} and
+     * {@link Parcel#writeException(Exception) Parcel.writeException} to propagate
+     * exceptions back to the caller.</p>
+     *
      * @param code The action to perform.  This should
      * be a number between {@link #FIRST_CALL_TRANSACTION} and
      * {@link #LAST_CALL_TRANSACTION}.
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 51f96ee..c58153a 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -22,6 +22,8 @@
 import android.util.SizeF;
 import android.util.SparseArray;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
@@ -32,9 +34,14 @@
  * @see PersistableBundle
  */
 public final class Bundle extends BaseBundle implements Cloneable, Parcelable {
-    private static final int FLAG_HAS_FDS = 1 << 8;
-    private static final int FLAG_HAS_FDS_KNOWN = 1 << 9;
-    private static final int FLAG_ALLOW_FDS = 1 << 10;
+    @VisibleForTesting
+    static final int FLAG_HAS_FDS = 1 << 8;
+
+    @VisibleForTesting
+    static final int FLAG_HAS_FDS_KNOWN = 1 << 9;
+
+    @VisibleForTesting
+    static final int FLAG_ALLOW_FDS = 1 << 10;
 
     public static final Bundle EMPTY;
 
@@ -65,20 +72,42 @@
      * will be unparcelled on first contact, using the assigned ClassLoader.
      *
      * @param parcelledData a Parcel containing a Bundle
+     *
+     * @hide
      */
-    Bundle(Parcel parcelledData) {
+    @VisibleForTesting
+    public Bundle(Parcel parcelledData) {
         super(parcelledData);
-        mFlags = FLAG_HAS_FDS_KNOWN | FLAG_ALLOW_FDS;
-        if (mParcelledData.hasFileDescriptors()) {
-            mFlags |= FLAG_HAS_FDS;
-        }
+        mFlags = FLAG_ALLOW_FDS;
+        maybePrefillHasFds();
     }
 
-    /* package */ Bundle(Parcel parcelledData, int length) {
+    /**
+     * Constructor from a parcel for when the length is known *and is not stored in the parcel.*
+     * The other constructor that takes a parcel assumes the length is in the parcel.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public Bundle(Parcel parcelledData, int length) {
         super(parcelledData, length);
-        mFlags = FLAG_HAS_FDS_KNOWN | FLAG_ALLOW_FDS;
-        if (mParcelledData.hasFileDescriptors()) {
-            mFlags |= FLAG_HAS_FDS;
+        mFlags = FLAG_ALLOW_FDS;
+        maybePrefillHasFds();
+    }
+
+    /**
+     * If {@link #mParcelledData} is not null, copy the HAS FDS bit from it because it's fast.
+     * Otherwise (if {@link #mParcelledData} is already null), leave {@link #FLAG_HAS_FDS_KNOWN}
+     * unset, because scanning a map is slower.  We'll do it lazily in
+     * {@link #hasFileDescriptors()}.
+     */
+    private void maybePrefillHasFds() {
+        if (mParcelledData != null) {
+            if (mParcelledData.hasFileDescriptors()) {
+                mFlags |= FLAG_HAS_FDS | FLAG_HAS_FDS_KNOWN;
+            } else {
+                mFlags |= FLAG_HAS_FDS_KNOWN;
+            }
         }
     }
 
@@ -1213,10 +1242,8 @@
      */
     public void readFromParcel(Parcel parcel) {
         super.readFromParcelInner(parcel);
-        mFlags = FLAG_HAS_FDS_KNOWN | FLAG_ALLOW_FDS;
-        if (mParcelledData.hasFileDescriptors()) {
-            mFlags |= FLAG_HAS_FDS;
-        }
+        mFlags = FLAG_ALLOW_FDS;
+        maybePrefillHasFds();
     }
 
     @Override
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 866e20c..270e63f 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -71,4 +71,13 @@
     }
 
     private long mNativeContext;
+
+    private static native void native_report_sysprop_change();
+
+    /**
+     * Notifies listeners that a system property has changed
+     */
+    public static void reportSyspropChanged() {
+        native_report_sysprop_change();
+    }
 }
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 10331b9..fae9d53 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -291,7 +291,7 @@
     private static native void nativeWriteFloat(long nativePtr, float val);
     @FastNative
     private static native void nativeWriteDouble(long nativePtr, double val);
-    private static native void nativeWriteString(long nativePtr, String val);
+    static native void nativeWriteString(long nativePtr, String val);
     private static native void nativeWriteStrongBinder(long nativePtr, IBinder val);
     private static native long nativeWriteFileDescriptor(long nativePtr, FileDescriptor val);
 
@@ -306,7 +306,7 @@
     private static native float nativeReadFloat(long nativePtr);
     @CriticalNative
     private static native double nativeReadDouble(long nativePtr);
-    private static native String nativeReadString(long nativePtr);
+    static native String nativeReadString(long nativePtr);
     private static native IBinder nativeReadStrongBinder(long nativePtr);
     private static native FileDescriptor nativeReadFileDescriptor(long nativePtr);
 
@@ -339,6 +339,33 @@
     };
 
     /**
+     * @hide
+     */
+    public static class ReadWriteHelper {
+        public static final ReadWriteHelper DEFAULT = new ReadWriteHelper();
+
+        /**
+         * Called when writing a string to a parcel. Subclasses wanting to write a string
+         * must use {@link #writeStringNoHelper(String)} to avoid
+         * infinity recursive calls.
+         */
+        public void writeString(Parcel p, String s) {
+            nativeWriteString(p.mNativePtr, s);
+        }
+
+        /**
+         * Called when reading a string to a parcel. Subclasses wanting to read a string
+         * must use {@link #readStringNoHelper()} to avoid
+         * infinity recursive calls.
+         */
+        public String readString(Parcel p) {
+            return nativeReadString(p.mNativePtr);
+        }
+    }
+
+    private ReadWriteHelper mReadWriteHelper = ReadWriteHelper.DEFAULT;
+
+    /**
      * Retrieve a new Parcel object from the pool.
      */
     public static Parcel obtain() {
@@ -352,6 +379,7 @@
                     if (DEBUG_RECYCLE) {
                         p.mStack = new RuntimeException();
                     }
+                    p.mReadWriteHelper = ReadWriteHelper.DEFAULT;
                     return p;
                 }
             }
@@ -385,6 +413,25 @@
         }
     }
 
+    /**
+     * Set a {@link ReadWriteHelper}, which can be used to avoid having duplicate strings, for
+     * example.
+     *
+     * @hide
+     */
+    public void setReadWriteHelper(ReadWriteHelper helper) {
+        mReadWriteHelper = helper != null ? helper : ReadWriteHelper.DEFAULT;
+    }
+
+    /**
+     * @return whether this parcel has a {@link ReadWriteHelper}.
+     *
+     * @hide
+     */
+    public boolean hasReadWriteHelper() {
+        return (mReadWriteHelper != null) && (mReadWriteHelper != ReadWriteHelper.DEFAULT);
+    }
+
     /** @hide */
     public static native long getGlobalAllocSize();
 
@@ -625,6 +672,17 @@
      * growing dataCapacity() if needed.
      */
     public final void writeString(String val) {
+        mReadWriteHelper.writeString(this, val);
+    }
+
+    /**
+     * Write a string without going though a {@link ReadWriteHelper}.  Subclasses of
+     * {@link ReadWriteHelper} must use this method instead of {@link #writeString} to avoid
+     * infinity recursive calls.
+     *
+     * @hide
+     */
+    public void writeStringNoHelper(String val) {
         nativeWriteString(mNativePtr, val);
     }
 
@@ -1782,6 +1840,7 @@
      * <li>{@link IllegalStateException}
      * <li>{@link NullPointerException}
      * <li>{@link SecurityException}
+     * <li>{@link UnsupportedOperationException}
      * <li>{@link NetworkOnMainThreadException}
      * </ul>
      *
@@ -1996,6 +2055,17 @@
      * Read a string value from the parcel at the current dataPosition().
      */
     public final String readString() {
+        return mReadWriteHelper.readString(this);
+    }
+
+    /**
+     * Read a string without going though a {@link ReadWriteHelper}.  Subclasses of
+     * {@link ReadWriteHelper} must use this method instead of {@link #readString} to avoid
+     * infinity recursive calls.
+     *
+     * @hide
+     */
+    public String readStringNoHelper() {
         return nativeReadString(mNativePtr);
     }
 
@@ -2996,6 +3066,7 @@
         if (mOwnsNativeParcelObject) {
             updateNativeSize(nativeFreeBuffer(mNativePtr));
         }
+        mReadWriteHelper = ReadWriteHelper.DEFAULT;
     }
 
     private void destroy() {
@@ -3006,6 +3077,7 @@
             }
             mNativePtr = 0;
         }
+        mReadWriteHelper = null;
     }
 
     @Override
diff --git a/core/java/android/os/SharedMemory.java b/core/java/android/os/SharedMemory.java
index 712bbaa..459aeb0 100644
--- a/core/java/android/os/SharedMemory.java
+++ b/core/java/android/os/SharedMemory.java
@@ -28,6 +28,7 @@
 import java.io.FileDescriptor;
 import java.nio.ByteBuffer;
 import java.nio.DirectByteBuffer;
+import java.nio.NioUtils;
 
 import sun.misc.Cleaner;
 
@@ -191,11 +192,16 @@
     }
 
     /**
-     * Creates an mmap of the SharedMemory with the specified prot, offset, and length.
+     * Creates an mmap of the SharedMemory with the specified prot, offset, and length. This will
+     * always produce a new ByteBuffer window to the backing shared memory region. Every call
+     * to map() may be paired with a call to {@link #unmap(ByteBuffer)} when the ByteBuffer
+     * returned by map() is no longer needed.
      *
      * @param prot A bitwise-or'd combination of PROT_READ, PROT_WRITE, PROT_EXEC, or PROT_NONE.
-     * @param offset The offset into the shared memory to begin mapping
-     * @param length The length of the region to map
+     * @param offset The offset into the shared memory to begin mapping. Must be >= 0 and less than
+     *         getSize().
+     * @param length The length of the region to map. Must be > 0 and offset + length must not
+     *         exceed getSize().
      * @return A ByteBuffer mapping.
      * @throws ErrnoException if the mmap call failed.
      */
@@ -203,7 +209,7 @@
         checkOpen();
         validateProt(prot);
         if (offset < 0) {
-            throw new IllegalArgumentException("Offset must be > 0");
+            throw new IllegalArgumentException("Offset must be >= 0");
         }
         if (length <= 0) {
             throw new IllegalArgumentException("Length must be > 0");
@@ -218,15 +224,16 @@
     }
 
     /**
-     * Unmaps a buffer previously returned by {@link #map(int, int, int)}
+     * Unmaps a buffer previously returned by {@link #map(int, int, int)}. This will immediately
+     * release the backing memory of the ByteBuffer, invalidating all references to it. Only
+     * call this method if there are no duplicates of the ByteBuffer in use and don't
+     * access the ByteBuffer after calling this method.
+     *
      * @param buffer The buffer to unmap
      */
     public static void unmap(@NonNull ByteBuffer buffer) {
         if (buffer instanceof DirectByteBuffer) {
-            Cleaner cleaner = ((DirectByteBuffer) buffer).cleaner();
-            if (cleaner != null) {
-                cleaner.clean();
-            }
+            NioUtils.freeDirectBuffer(buffer);
         } else {
             throw new IllegalArgumentException(
                     "ByteBuffer wasn't created by #map(int, int, int); can't unmap");
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 6372113..8533c7e 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1662,8 +1662,8 @@
      * itself on the given storage volume. This value is typically larger than
      * {@link File#getUsableSpace()}, since the system may be willing to delete
      * cached files to satisfy an allocation request. You can then allocate
-     * space for yourself using {@link #allocateBytes(UUID, long, int)} or
-     * {@link #allocateBytes(FileDescriptor, long, int)}.
+     * space for yourself using {@link #allocateBytes(UUID, long)} or
+     * {@link #allocateBytes(FileDescriptor, long)}.
      * <p>
      * This method is best used as a pre-flight check, such as deciding if there
      * is enough space to store an entire music album before you allocate space
@@ -1690,8 +1690,8 @@
      *            UUID for a specific path can be obtained using
      *            {@link #getUuidForPath(File)}.
      * @return the maximum number of new bytes that the calling app can allocate
-     *         using {@link #allocateBytes(UUID, long, int)} or
-     *         {@link #allocateBytes(FileDescriptor, long, int)}.
+     *         using {@link #allocateBytes(UUID, long)} or
+     *         {@link #allocateBytes(FileDescriptor, long)}.
      * @throws IOException when the storage device isn't present, or when it
      *             doesn't support allocating space.
      */
@@ -1724,11 +1724,11 @@
      * files necessary to satisfy your request.
      * <p>
      * Attempts to allocate disk space beyond the value returned by
-     * {@link #getAllocatableBytes(UUID, int)} will fail.
+     * {@link #getAllocatableBytes(UUID)} will fail.
      * <p>
      * Since multiple apps can be running simultaneously, this method may be
      * subject to race conditions. If possible, consider using
-     * {@link #allocateBytes(FileDescriptor, long, int)} which will guarantee
+     * {@link #allocateBytes(FileDescriptor, long)} which will guarantee
      * that bytes are allocated to an opened file.
      * <p>
      * If you're progressively allocating an unbounded amount of storage space
@@ -1742,7 +1742,7 @@
      * @throws IOException when the storage device isn't present, or when it
      *             doesn't support allocating space, or if the device had
      *             trouble allocating the requested space.
-     * @see #getAllocatableBytes(UUID, int)
+     * @see #getAllocatableBytes(UUID)
      */
     @WorkerThread
     public void allocateBytes(@NonNull UUID storageUuid, @BytesLong long bytes)
@@ -1772,7 +1772,7 @@
      * necessary to satisfy your request.
      * <p>
      * Attempts to allocate disk space beyond the value returned by
-     * {@link #getAllocatableBytes(UUID, int)} will fail.
+     * {@link #getAllocatableBytes(UUID)} will fail.
      * <p>
      * This method guarantees that bytes have been allocated to the opened file,
      * otherwise it will throw if fast allocation is not possible. Fast
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index ad9844e..99fcdad 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -44,6 +44,7 @@
 import android.os.OperationCanceledException;
 import android.os.Parcel;
 import android.os.ParcelFileDescriptor;
+import android.os.ParcelFileDescriptor.OnCloseListener;
 import android.os.Parcelable;
 import android.os.ParcelableException;
 import android.os.RemoteException;
diff --git a/core/java/android/provider/DocumentsProvider.java b/core/java/android/provider/DocumentsProvider.java
index 2298134..81b1921 100644
--- a/core/java/android/provider/DocumentsProvider.java
+++ b/core/java/android/provider/DocumentsProvider.java
@@ -66,8 +66,6 @@
 import libcore.io.IoUtils;
 
 import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
 import java.util.LinkedList;
 import java.util.Objects;
 
@@ -635,30 +633,6 @@
     }
 
     /**
-     * Returns metadata for arbitrary file given its stream and mimetype.
-     *
-     * <p><b>Note: Providers should only call this with streams for locally cached resources.
-     * Use of network backed streams is inadvisable for performance reasons.
-     *
-     * @param stream The input stream. Should be backed by locally cached content.
-     *       Client retains ownership of the stream.
-     * @param mimeType The mime type of the file.
-     * @param tags The list of tags to load, if known. Pass null to get a default set.
-     * @return Bundle containing any metadata found.
-     * @throws IOException in the event of an error reading metadata.
-     *
-     * @hide
-     */
-    protected Bundle getDocumentMetadataFromStream(InputStream stream, String mimeType)
-            throws IOException {
-        Bundle metadata = new Bundle();
-        // TODO: Remove the last null arg from MetadataReader. It was the "tags" value,
-        // the has been removed from the getDocumentMetadata method.
-        MetadataReader.getMetadata(metadata, stream, mimeType, null);
-        return metadata;
-    }
-
-    /**
      * Return concrete MIME type of the requested document. Must match the value
      * of {@link Document#COLUMN_MIME_TYPE} for this document. The default
      * implementation queries {@link #queryDocument(String, String[])}, so
diff --git a/core/java/android/provider/FontsContract.java b/core/java/android/provider/FontsContract.java
index f909114..d8540ff 100644
--- a/core/java/android/provider/FontsContract.java
+++ b/core/java/android/provider/FontsContract.java
@@ -447,14 +447,14 @@
         public FontRequestCallback() {}
 
         /**
-         * Called then a Typeface request done via {@link #requestFont} is complete. Note that this
+         * Called then a Typeface request done via {@link #requestFonts} is complete. Note that this
          * method will not be called if {@link #onTypefaceRequestFailed(int)} is called instead.
          * @param typeface  The Typeface object retrieved.
          */
         public void onTypefaceRetrieved(Typeface typeface) {}
 
         /**
-         * Called when a Typeface request done via {@link #requestFont}} fails.
+         * Called when a Typeface request done via {@link #requestFonts}} fails.
          * @param reason One of {@link #FAIL_REASON_PROVIDER_NOT_FOUND},
          *               {@link #FAIL_REASON_FONT_NOT_FOUND},
          *               {@link #FAIL_REASON_FONT_LOAD_ERROR},
diff --git a/core/java/android/provider/MetadataReader.java b/core/java/android/provider/MetadataReader.java
index 5aa5f72..4f3a7d6 100644
--- a/core/java/android/provider/MetadataReader.java
+++ b/core/java/android/provider/MetadataReader.java
@@ -36,29 +36,33 @@
  */
 public final class MetadataReader {
 
-    private MetadataReader() {
-    }
+    private MetadataReader() {}
 
     private static final String[] DEFAULT_EXIF_TAGS = {
-            ExifInterface.TAG_IMAGE_WIDTH,
-            ExifInterface.TAG_IMAGE_LENGTH,
+            ExifInterface.TAG_APERTURE,
+            ExifInterface.TAG_COPYRIGHT,
             ExifInterface.TAG_DATETIME,
+            ExifInterface.TAG_EXPOSURE_TIME,
+            ExifInterface.TAG_FOCAL_LENGTH,
+            ExifInterface.TAG_F_NUMBER,
             ExifInterface.TAG_GPS_LATITUDE,
             ExifInterface.TAG_GPS_LATITUDE_REF,
             ExifInterface.TAG_GPS_LONGITUDE,
             ExifInterface.TAG_GPS_LONGITUDE_REF,
+            ExifInterface.TAG_IMAGE_LENGTH,
+            ExifInterface.TAG_IMAGE_WIDTH,
+            ExifInterface.TAG_ISO_SPEED_RATINGS,
             ExifInterface.TAG_MAKE,
             ExifInterface.TAG_MODEL,
-            ExifInterface.TAG_APERTURE,
-            ExifInterface.TAG_SHUTTER_SPEED_VALUE
+            ExifInterface.TAG_ORIENTATION,
+            ExifInterface.TAG_SHUTTER_SPEED_VALUE,
     };
 
-    private static final Map<String, Integer> TYPE_MAPPING = new HashMap<>();
-    private static final String[] ALL_KNOWN_EXIF_KEYS;
     private static final int TYPE_INT = 0;
     private static final int TYPE_DOUBLE = 1;
     private static final int TYPE_STRING = 2;
 
+    private static final Map<String, Integer> TYPE_MAPPING = new HashMap<>();
     static {
         // TODO: Move this over to ExifInterface.java
         // Since each ExifInterface item has a type, and there's currently no way to get the type
@@ -198,11 +202,19 @@
         TYPE_MAPPING.put(ExifInterface.TAG_RW2_SENSOR_RIGHT_BORDER, TYPE_INT);
         TYPE_MAPPING.put(ExifInterface.TAG_RW2_SENSOR_TOP_BORDER, TYPE_INT);
         TYPE_MAPPING.put(ExifInterface.TAG_RW2_ISO, TYPE_INT);
-        ALL_KNOWN_EXIF_KEYS = TYPE_MAPPING.keySet().toArray(new String[TYPE_MAPPING.size()]);
     }
     private static final String JPG_MIME_TYPE = "image/jpg";
     private static final String JPEG_MIME_TYPE = "image/jpeg";
 
+
+    /**
+     * Returns true if caller can generally expect to get metadata results
+     * for the supplied mimetype.
+     */
+    public static boolean isSupportedMimeType(String mimeType) {
+        return JPG_MIME_TYPE.equals(mimeType) || JPEG_MIME_TYPE.equals(mimeType);
+    }
+
     /**
      * Generic metadata retrieval method that can retrieve any available metadata from a given doc
      * Currently only functions for exifdata
@@ -211,27 +223,14 @@
      * @param stream InputStream containing a file
      * @param mimeType type of the given file
      * @param tags a variable amount of keys to differentiate which tags the user wants
-     *             if null, returns a default set of data from the following keys:
-     *             Exif data:
-     *             ExifInterface.TAG_IMAGE_WIDTH,
-     *             ExifInterface.TAG_IMAGE_LENGTH,
-     *             ExifInterface.TAG_DATETIME,
-     *             ExifInterface.TAG_GPS_LATITUDE,
-     *             ExifInterface.TAG_GPS_LATITUDE_REF,
-     *             ExifInterface.TAG_GPS_LONGITUDE,
-     *             ExifInterface.TAG_GPS_LONGITUDE_REF,
-     *             ExifInterface.TAG_MAKE,
-     *             ExifInterface.TAG_MODEL,
-     *             ExifInterface.TAG_APERTURE,
-     *             ExifInterface.TAG_SHUTTER_SPEED_VALUE
+     *             if null, returns a default set of data. See {@link DEFAULT_EXIF_TAGS}.
      * @throws IOException when the file doesn't exist
      */
     public static void getMetadata(Bundle metadata, InputStream stream, String mimeType,
             @Nullable String[] tags) throws IOException {
-        List<String> metadataTypes = new ArrayList();
-        if (mimeType.equals(JPG_MIME_TYPE) || mimeType.equals(JPEG_MIME_TYPE)) {
-            ExifInterface exifInterface = new ExifInterface(stream);
-            Bundle exifData = getExifData(exifInterface, tags);
+        List<String> metadataTypes = new ArrayList<>();
+        if (isSupportedMimeType(mimeType)) {
+            Bundle exifData = getExifData(stream, tags);
             if (exifData.size() > 0) {
                 metadata.putBundle(DocumentsContract.METADATA_EXIF, exifData);
                 metadataTypes.add(DocumentsContract.METADATA_EXIF);
@@ -246,27 +245,17 @@
     /**
      * Helper method that is called if getMetadata is called for an image mimeType.
      *
-     * @param exif the bundle to which we add exif data.
-     * @param exifInterface an ExifInterface for an image
+     * @param stream the input stream from which to extra data.
      * @param tags a list of ExifInterface tags that are used to retrieve data.
-     *             if null, returns a default set of data from the following keys:
-     *             ExifInterface.TAG_IMAGE_WIDTH,
-     *             ExifInterface.TAG_IMAGE_LENGTH,
-     *             ExifInterface.TAG_DATETIME,
-     *             ExifInterface.TAG_GPS_LATITUDE,
-     *             ExifInterface.TAG_GPS_LATITUDE_REF,
-     *             ExifInterface.TAG_GPS_LONGITUDE,
-     *             ExifInterface.TAG_GPS_LONGITUDE_REF,
-     *             ExifInterface.TAG_MAKE,
-     *             ExifInterface.TAG_MODEL,
-     *             ExifInterface.TAG_APERTURE,
-     *             ExifInterface.TAG_SHUTTER_SPEED_VALUE
+     *             if null, returns a default set of data. See {@link DEFAULT_EXIF_TAGS}.
      */
-    private static Bundle getExifData(ExifInterface exifInterface, @Nullable String[] tags)
+    private static Bundle getExifData(InputStream stream, @Nullable String[] tags)
             throws IOException {
         if (tags == null) {
             tags = DEFAULT_EXIF_TAGS;
         }
+
+        ExifInterface exifInterface = new ExifInterface(stream);
         Bundle exif = new Bundle();
         for (String tag : tags) {
             if (TYPE_MAPPING.get(tag).equals(TYPE_INT)) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8869a6b..88aafdc 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -576,21 +576,6 @@
             "android.settings.INPUT_METHOD_SUBTYPE_SETTINGS";
 
     /**
-     * Activity Action: Show a dialog to select input method.
-     * <p>
-     * In some cases, a matching Activity may not exist, so ensure you
-     * safeguard against this.
-     * <p>
-     * Input: Nothing.
-     * <p>
-     * Output: Nothing.
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
-    public static final String ACTION_SHOW_INPUT_METHOD_PICKER =
-            "android.settings.SHOW_INPUT_METHOD_PICKER";
-
-    /**
      * Activity Action: Show settings to manage the user input dictionary.
      * <p>
      * Starting with {@link android.os.Build.VERSION_CODES#KITKAT},
diff --git a/core/java/android/security/OWNERS b/core/java/android/security/OWNERS
new file mode 100644
index 0000000..7120376
--- /dev/null
+++ b/core/java/android/security/OWNERS
@@ -0,0 +1,4 @@
+per-file NetworkSecurityPolicy.java = cbrubaker@google.com
+per-file NetworkSecurityPolicy.java = klyubin@google.com
+per-file FrameworkNetworkSecurityPolicy.java = cbrubaker@google.com
+per-file FrameworkNetworkSecurityPolicy.java = klyubin@google.com
diff --git a/core/java/android/security/net/config/OWNERS b/core/java/android/security/net/config/OWNERS
new file mode 100644
index 0000000..5350373
--- /dev/null
+++ b/core/java/android/security/net/config/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+cbrubaker@google.com
+klyubin@google.com
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index a80ef03..c068e6a 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -179,11 +179,18 @@
  * should not contain fields for username, password, and credit card information. The reason for
  * this rule is that a malicious app could draft a view structure where the credit card fields
  * are not visible, so when the user selects a dataset from the username UI, the credit card info is
- * released to the application without the user knowledge. Similar, it's recommended to always
+ * released to the application without the user knowledge. Similarly, it's recommended to always
  * protect a dataset that contains sensitive information by requiring dataset authentication
- * (see {@link Dataset.Builder#setAuthentication(android.content.IntentSender)}).
+ * (see {@link Dataset.Builder#setAuthentication(android.content.IntentSender)}), and to include
+ * info about the "primary" field of the partition in the custom presentation for "secondary"
+ * fields &mdash; that would prevent a malicious app from getting the "primary" fields without the
+ * user realizing they're being released (for example, a malicious app could have fields for a
+ * credit card number, verification code, and expiration date crafted in a way that just the latter
+ * is visible; by explicitly indicating the expiration date is related to a given credit card
+ * number, the service would be providing a visual clue for the users to check what would be
+ * released upon selecting that field).
  *
- * <p>When the service detects that a screen have multiple partitions, it should return a
+ * <p>When the service detects that a screen has multiple partitions, it should return a
  * {@link FillResponse} with just the datasets for the partition that originated the request (i.e.,
  * the partition that has the {@link android.app.assist.AssistStructure.ViewNode} whose
  * {@link android.app.assist.AssistStructure.ViewNode#isFocused()} returns {@code true}); then if
@@ -236,6 +243,36 @@
  * <p>When the service returns multiple {@link FillResponse}, the last one overrides the previous;
  * that's why the {@link SaveInfo} in the 2nd request above has the info for both partitions.
  *
+ * <h3>Package verification</h3>
+ *
+ * <p>When autofilling app-specific data (like username and password), the service must verify
+ * the authenticity of the request by obtaining all signing certificates of the app being
+ * autofilled, and only fulfilling the request when they match the values that were
+ * obtained when the data was first saved &mdash; such verification is necessary to avoid phishing
+ * attempts by apps that were sideloaded in the device with the same package name of another app.
+ * Here's an example on how to achieve that by hashing the signing certificates:
+ *
+ * <pre class="prettyprint">
+ * private String getCertificatesHash(String packageName) throws Exception {
+ *   PackageManager pm = mContext.getPackageManager();
+ *   PackageInfo info = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+ *   ArrayList<String> hashes = new ArrayList<>(info.signatures.length);
+ *   for (Signature sig : info.signatures) {
+ *     byte[] cert = sig.toByteArray();
+ *     MessageDigest md = MessageDigest.getInstance("SHA-256");
+ *     md.update(cert);
+ *     hashes.add(toHexString(md.digest()));
+ *   }
+ *   Collections.sort(hashes);
+ *   StringBuilder hash = new StringBuilder();
+ *   for (int i = 0; i < hashes.size(); i++) {
+ *     hash.append(hashes.get(i));
+ *   }
+ *   return hash.toString();
+ * }
+ *
+ * </pre>
+ *
  * <h3>Ignoring views</h3>
  *
  * <p>If the service find views that cannot be autofilled (for example, a text field representing
diff --git a/core/java/android/service/autofill/CustomDescription.java b/core/java/android/service/autofill/CustomDescription.java
index 0edb154..3da0b5e 100644
--- a/core/java/android/service/autofill/CustomDescription.java
+++ b/core/java/android/service/autofill/CustomDescription.java
@@ -32,7 +32,7 @@
  *
  * <p>This is useful when the autofill service needs to show a detailed view of what would be saved;
  * for example, when the screen contains a credit card, it could display a logo of the credit card
- * bank, the last for digits of the credit card number, and its expiration number.
+ * bank, the last four digits of the credit card number, and its expiration number.
  *
  * <p>A custom description is made of 2 parts:
  * <ul>
@@ -63,16 +63,16 @@
  * // Image child - different logo for each bank, based on credit card prefix
  * builder.addChild(R.id.templateccLogo,
  *   new ImageTransformation.Builder(ccNumberId)
- *     .addOption("^4815.*$", R.drawable.ic_credit_card_logo1)
- *     .addOption("^1623.*$", R.drawable.ic_credit_card_logo2)
- *     .addOption("^42.*$", R.drawable.ic_credit_card_logo3);
+ *     .addOption(Pattern.compile(""^4815.*$"), R.drawable.ic_credit_card_logo1)
+ *     .addOption(Pattern.compile(""^1623.*$"), R.drawable.ic_credit_card_logo2)
+ *     .addOption(Pattern.compile(""^42.*$"), R.drawable.ic_credit_card_logo3);
  * // Masked credit card number (as .....LAST_4_DIGITS)
  * builder.addChild(R.id.templateCcNumber, new CharSequenceTransformation.Builder()
- *     .addField(ccNumberId, "^.*(\\d\\d\\d\\d)$", "...$1")
+ *     .addField(ccNumberId, Pattern.compile(""^.*(\\d\\d\\d\\d)$"), "...$1")
  * // Expiration date as MM / YYYY:
  * builder.addChild(R.id.templateExpDate, new CharSequenceTransformation.Builder()
- *     .addField(ccExpMonthId, "^(\\d\\d)$", "Exp: $1")
- *     .addField(ccExpYearId, "^(\\d\\d)$", "/$1");
+ *     .addField(ccExpMonthId, Pattern.compile(""^(\\d\\d)$"), "Exp: $1")
+ *     .addField(ccExpYearId, Pattern.compile(""^(\\d\\d)$"), "/$1");
  * </pre>
  *
  * <p>See {@link ImageTransformation}, {@link CharSequenceTransformation} for more info about these
diff --git a/core/java/android/service/autofill/FillRequest.java b/core/java/android/service/autofill/FillRequest.java
index fd6da05..1c32fe0 100644
--- a/core/java/android/service/autofill/FillRequest.java
+++ b/core/java/android/service/autofill/FillRequest.java
@@ -61,9 +61,8 @@
      * <p>An explicit autofill request is triggered when the
      * {@link android.view.autofill.AutofillManager#requestAutofill(View)} or
      * {@link android.view.autofill.AutofillManager#requestAutofill(View, int, android.graphics.Rect)}
-     * is called. For example, standard {@link android.widget.TextView} views that use
-     * an {@link android.widget.Editor} shows an {@code AUTOFILL} option in the overflow menu that
-     * triggers such request.
+     * is called. For example, standard {@link android.widget.TextView} views show an
+     * {@code AUTOFILL} option in the overflow menu that triggers such request.
      */
     public static final int FLAG_MANUAL_REQUEST = 0x1;
 
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 65b9cb2..b6a9a26 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -170,7 +170,7 @@
          * @param ids id of Views that when focused will display the authentication UI affordance.
          *
          * @return This builder.
-         * @throw {@link IllegalArgumentException} if {@code ids} is {@code null} or empty, or if
+         * @throws IllegalArgumentException if {@code ids} is {@code null} or empty, or if
          * neither {@code authentication} nor {@code presentation} is non-{@code null}.
          *
          * @see android.app.PendingIntent#getIntentSender()
diff --git a/core/java/android/service/autofill/SimpleRegexValidator.java b/core/java/android/service/autofill/RegexValidator.java
similarity index 79%
rename from core/java/android/service/autofill/SimpleRegexValidator.java
rename to core/java/android/service/autofill/RegexValidator.java
index ef8c52c9..9dfe78d 100644
--- a/core/java/android/service/autofill/SimpleRegexValidator.java
+++ b/core/java/android/service/autofill/RegexValidator.java
@@ -34,9 +34,9 @@
  *
  * <p>See {@link SaveInfo.Builder#setValidator(Validator)} for examples.
  */
-public final class SimpleRegexValidator extends InternalValidator implements Validator, Parcelable {
+public final class RegexValidator extends InternalValidator implements Validator, Parcelable {
 
-    private static final String TAG = "SimpleRegexValidator";
+    private static final String TAG = "RegexValidator";
 
     private final AutofillId mId;
     private final Pattern mRegex;
@@ -49,7 +49,7 @@
      * matches the contents of the field identified by {@code id}, it returns {@code true};
      * otherwise, it returns {@code false}.
       */
-    public SimpleRegexValidator(@NonNull AutofillId id, @NonNull Pattern regex) {
+    public RegexValidator(@NonNull AutofillId id, @NonNull Pattern regex) {
         mId = Preconditions.checkNotNull(id);
         mRegex = Preconditions.checkNotNull(regex);
     }
@@ -76,7 +76,7 @@
     public String toString() {
         if (!sDebug) return super.toString();
 
-        return "SimpleRegexValidator: [id=" + mId + ", regex=" + mRegex + "]";
+        return "RegexValidator: [id=" + mId + ", regex=" + mRegex + "]";
     }
 
     /////////////////////////////////////
@@ -93,17 +93,17 @@
         parcel.writeSerializable(mRegex);
     }
 
-    public static final Parcelable.Creator<SimpleRegexValidator> CREATOR =
-            new Parcelable.Creator<SimpleRegexValidator>() {
+    public static final Parcelable.Creator<RegexValidator> CREATOR =
+            new Parcelable.Creator<RegexValidator>() {
         @Override
-        public SimpleRegexValidator createFromParcel(Parcel parcel) {
-            return new SimpleRegexValidator(parcel.readParcelable(null),
+        public RegexValidator createFromParcel(Parcel parcel) {
+            return new RegexValidator(parcel.readParcelable(null),
                     (Pattern) parcel.readSerializable());
         }
 
         @Override
-        public SimpleRegexValidator[] newArray(int size) {
-            return new SimpleRegexValidator[size];
+        public RegexValidator[] newArray(int size) {
+            return new RegexValidator[size];
         }
     };
 }
diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java
index f8a94d6..e0a0730 100644
--- a/core/java/android/service/autofill/SaveInfo.java
+++ b/core/java/android/service/autofill/SaveInfo.java
@@ -470,7 +470,7 @@
          * <p>Validator for a credit number that must have exactly 16 digits:
          *
          * <pre class="prettyprint">
-         * Validator validator = new SimpleRegexValidator(ccNumberId, "^\\d{16}$")
+         * Validator validator = new RegexValidator(ccNumberId, Pattern.compile(""^\\d{16}$"))
          * </pre>
          *
          * <p>Validator for a credit number that must pass a Luhn checksum and either have
@@ -483,8 +483,8 @@
          *   and(
          *     new LuhnChecksumValidator(ccNumberId),
          *     or(
-         *       new SimpleRegexValidator(ccNumberId, "^\\d{16}$"),
-         *       new SimpleRegexValidator(ccNumberId, "^108\\d{12}$")
+         *       new RegexValidator(ccNumberId, Pattern.compile(""^\\d{16}$")),
+         *       new RegexValidator(ccNumberId, Pattern.compile(""^108\\d{12}$"))
          *     )
          *   );
          * </pre>
@@ -496,7 +496,7 @@
          * Validator validator =
          *   and(
          *     new LuhnChecksumValidator(ccNumberId),
-         *     new SimpleRegexValidator(ccNumberId, "^(\\d{16}|108\\d{12})$")
+         *     new RegexValidator(ccNumberId, Pattern.compile(""^(\\d{16}|108\\d{12})$"))
          *   );
          * </pre>
          *
@@ -508,10 +508,10 @@
          *
          * Validator validator =
          *   and(
-         *     new SimpleRegexValidator(ccNumberId1, "^\\d{4}$"),
-         *     new SimpleRegexValidator(ccNumberId2, "^\\d{4}$"),
-         *     new SimpleRegexValidator(ccNumberId3, "^\\d{4}$"),
-         *     new SimpleRegexValidator(ccNumberId4, "^\\d{4}$")
+         *     new RegexValidator(ccNumberId1, Pattern.compile(""^\\d{4}$")),
+         *     new RegexValidator(ccNumberId2, Pattern.compile(""^\\d{4}$")),
+         *     new RegexValidator(ccNumberId3, Pattern.compile(""^\\d{4}$")),
+         *     new RegexValidator(ccNumberId4, Pattern.compile(""^\\d{4}$"))
          *   );
          * </pre>
          *
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 855c87b..a5223fd 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -27,6 +27,7 @@
 import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
 import android.app.Service;
+import android.companion.CompanionDeviceManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -64,7 +65,7 @@
  * A service that receives calls from the system when new notifications are
  * posted or removed, or their ranking changed.
  * <p>To extend this class, you must declare the service in your manifest file with
- * the {@link Manifest.permission#BIND_NOTIFICATION_LISTENER_SERVICE} permission
+ * the {@link android.Manifest.permission#BIND_NOTIFICATION_LISTENER_SERVICE} permission
  * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
  * <pre>
  * &lt;service android:name=".NotificationListener"
diff --git a/core/java/android/service/wallpaper/IWallpaperEngine.aidl b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
index eff52e6..fb6f637 100644
--- a/core/java/android/service/wallpaper/IWallpaperEngine.aidl
+++ b/core/java/android/service/wallpaper/IWallpaperEngine.aidl
@@ -30,5 +30,6 @@
     void dispatchPointer(in MotionEvent event);
     void dispatchWallpaperCommand(String action, int x, int y,
             int z, in Bundle extras);
+    void requestWallpaperColors();
     void destroy();
 }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 424967f..65d66dc 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -105,6 +105,7 @@
     private static final int MSG_WINDOW_RESIZED = 10030;
     private static final int MSG_WINDOW_MOVED = 10035;
     private static final int MSG_TOUCH_EVENT = 10040;
+    private static final int MSG_REQUEST_WALLPAPER_COLORS = 10050;
     
     private final ArrayList<Engine> mActiveEngines
             = new ArrayList<Engine>();
@@ -268,7 +269,7 @@
             }
 
             @Override
-            public void onInputEvent(InputEvent event) {
+            public void onInputEvent(InputEvent event, int displayId) {
                 boolean handled = false;
                 try {
                     if (event instanceof MotionEvent
@@ -626,8 +627,7 @@
                 }
                 Message msg = mCaller.obtainMessageO(MSG_TOUCH_EVENT, event);
                 mCaller.sendMessage(msg);
-            } else {
-                event.recycle();
+            } else {event.recycle();
             }
         }
 
@@ -1192,6 +1192,11 @@
             }
         }
 
+        public void requestWallpaperColors() {
+            Message msg = mCaller.obtainMessage(MSG_REQUEST_WALLPAPER_COLORS);
+            mCaller.sendMessage(msg);
+        }
+
         public void destroy() {
             Message msg = mCaller.obtainMessage(DO_DETACH);
             mCaller.sendMessage(msg);
@@ -1210,7 +1215,6 @@
                     mEngine = engine;
                     mActiveEngines.add(engine);
                     engine.attach(this);
-                    engine.notifyColorsChanged();
                     return;
                 }
                 case DO_DETACH: {
@@ -1268,6 +1272,16 @@
                     }
                     ev.recycle();
                 } break;
+                case MSG_REQUEST_WALLPAPER_COLORS: {
+                    if (mConnection == null) {
+                        break;
+                    }
+                    try {
+                        mConnection.onWallpaperColorsChanged(mEngine.onComputeColors());
+                    } catch (RemoteException e) {
+                        // Connection went away, nothing to do in here.
+                    }
+                } break;
                 default :
                     Log.w(TAG, "Unknown message type " + message.what);
             }
diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java
index 1f1863c..2900c83 100644
--- a/core/java/android/speech/tts/AudioPlaybackHandler.java
+++ b/core/java/android/speech/tts/AudioPlaybackHandler.java
@@ -106,6 +106,7 @@
             final PlaybackQueueItem item = it.next();
             if (item.getCallerIdentity() == callerIdentity) {
                 it.remove();
+                stop(item);
             }
         }
     }
diff --git a/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java b/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java
index f52638b..704a1da 100644
--- a/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java
+++ b/core/java/android/speech/tts/SynthesisPlaybackQueueItem.java
@@ -21,6 +21,7 @@
 import android.util.Log;
 
 import java.util.LinkedList;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
@@ -70,6 +71,11 @@
     // wait for the next one.
     private ConcurrentLinkedQueue<ProgressMarker> markerList = new ConcurrentLinkedQueue<>();
 
+    private static final int NOT_RUN = 0;
+    private static final int RUN_CALLED = 1;
+    private static final int STOP_CALLED = 2;
+    private final AtomicInteger mRunState = new AtomicInteger(NOT_RUN);
+
     SynthesisPlaybackQueueItem(AudioOutputParams audioParams, int sampleRate,
             int audioFormat, int channelCount, UtteranceProgressDispatcher dispatcher,
             Object callerIdentity, AbstractEventLogger logger) {
@@ -88,6 +94,11 @@
 
     @Override
     public void run() {
+        if (!mRunState.compareAndSet(NOT_RUN, RUN_CALLED)) {
+            // stop() was already called before run(). Do nothing and just finish.
+            return;
+        }
+
         final UtteranceProgressDispatcher dispatcher = getDispatcher();
         dispatcher.dispatchOnStart();
 
@@ -120,6 +131,12 @@
 
         mAudioTrack.waitAndRelease();
 
+        dispatchEndStatus();
+    }
+
+    private void dispatchEndStatus() {
+        final UtteranceProgressDispatcher dispatcher = getDispatcher();
+
         if (mStatusCode == TextToSpeech.SUCCESS) {
             dispatcher.dispatchOnSuccess();
         } else if(mStatusCode == TextToSpeech.STOPPED) {
@@ -140,6 +157,13 @@
             mStopped = true;
             mStatusCode = statusCode;
 
+            if (mRunState.getAndSet(STOP_CALLED) == NOT_RUN) {
+                // Dispatch the status code and just finish without signaling
+                // if run() has not even started.
+                dispatchEndStatus();
+                return;
+            }
+
             // Wake up the audio playback thread if it was waiting on take().
             // take() will return null since mStopped was true, and will then
             // break out of the data write loop.
diff --git a/core/java/android/text/InputFilter.java b/core/java/android/text/InputFilter.java
index d773158..a507f2b 100644
--- a/core/java/android/text/InputFilter.java
+++ b/core/java/android/text/InputFilter.java
@@ -16,7 +16,9 @@
 
 package android.text;
 
-import android.annotation.Nullable;
+import android.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
 
 import java.util.Locale;
 
@@ -64,7 +66,8 @@
          * Constructs a locale-specific AllCaps filter, to make sure capitalization rules of that
          * locale are used for transforming the sequence.
          */
-        public AllCaps(@Nullable Locale locale) {
+        public AllCaps(@NonNull Locale locale) {
+            Preconditions.checkNotNull(locale);
             mLocale = locale;
         }
 
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index 2dc3f60..d612fc7 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -152,6 +152,17 @@
      */
     public static float getDesiredWidth(CharSequence source, int start, int end, TextPaint paint,
             TextDirectionHeuristic textDir) {
+        return getDesiredWidthWithLimit(source, start, end, paint, textDir, Float.MAX_VALUE);
+    }
+    /**
+     * Return how wide a layout must be in order to display the
+     * specified text slice with one line per paragraph.
+     *
+     * If the measured width exceeds given limit, returns limit value instead.
+     * @hide
+     */
+    public static float getDesiredWidthWithLimit(CharSequence source, int start, int end,
+            TextPaint paint, TextDirectionHeuristic textDir, float upperLimit) {
         float need = 0;
 
         int next;
@@ -163,6 +174,9 @@
 
             // note, omits trailing paragraph char
             float w = measurePara(paint, source, i, next, textDir);
+            if (w > upperLimit) {
+                return upperLimit;
+            }
 
             if (w > need)
                 need = w;
@@ -1451,6 +1465,16 @@
     }
 
     /**
+     * Return the vertical position of the bottom of the specified line without the line spacing
+     * added.
+     *
+     * @hide
+     */
+    public final int getLineBottomWithoutSpacing(int line) {
+        return getLineTop(line + 1) - getLineExtra(line);
+    }
+
+    /**
      * Return the vertical position of the baseline of the specified line.
      */
     public final int getLineBaseline(int line) {
@@ -1593,13 +1617,12 @@
      * but can be multiple discontinuous lines in text with multiple
      * directionalities.
      */
-    public void getCursorPath(int point, Path dest,
-                              CharSequence editingBuffer) {
+    public void getCursorPath(final int point, final Path dest, final CharSequence editingBuffer) {
         dest.reset();
 
         int line = getLineForOffset(point);
         int top = getLineTop(line);
-        int bottom = getLineTop(line+1);
+        int bottom = getLineBottomWithoutSpacing(line);
 
         boolean clamped = shouldClampCursor(line);
         float h1 = getPrimaryHorizontal(point, clamped) - 0.5f;
@@ -1719,11 +1742,11 @@
             start = temp;
         }
 
-        int startline = getLineForOffset(start);
-        int endline = getLineForOffset(end);
+        final int startline = getLineForOffset(start);
+        final int endline = getLineForOffset(end);
 
         int top = getLineTop(startline);
-        int bottom = getLineBottom(endline);
+        int bottom = getLineBottomWithoutSpacing(endline);
 
         if (startline == endline) {
             addSelection(startline, start, end, top, bottom, dest);
@@ -1747,10 +1770,9 @@
             }
 
             top = getLineTop(endline);
-            bottom = getLineBottom(endline);
+            bottom = getLineBottomWithoutSpacing(endline);
 
-            addSelection(endline, getLineStart(endline), end,
-                         top, bottom, dest);
+            addSelection(endline, getLineStart(endline), end, top, bottom, dest);
 
             if (getParagraphDirection(endline) == DIR_RIGHT_TO_LEFT)
                 dest.addRect(width, top, getLineRight(endline), bottom, Path.Direction.CW);
@@ -2059,9 +2081,11 @@
 
         final String ellipsisString = TextUtils.getEllipsisString(method);
         final int ellipsisStringLen = ellipsisString.length();
+        // Use the ellipsis string only if there are that at least as many characters to replace.
+        final boolean useEllipsisString = ellipsisCount >= ellipsisStringLen;
         for (int i = 0; i < ellipsisCount; i++) {
             final char c;
-            if (i < ellipsisStringLen && ellipsisCount <= ellipsisStringLen) {
+            if (useEllipsisString && i < ellipsisStringLen) {
                 c = ellipsisString.charAt(i);
             } else {
                 c = TextUtils.ELLIPSIS_FILLER;
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
index 48e5ca9..af26a88 100644
--- a/core/java/android/view/FocusFinder.java
+++ b/core/java/android/view/FocusFinder.java
@@ -193,6 +193,8 @@
     private View findNextUserSpecifiedFocus(ViewGroup root, View focused, int direction) {
         // check for user specified next focus
         View userSetNextFocus = focused.findUserSetNextFocus(root, direction);
+        View cycleCheck = userSetNextFocus;
+        boolean cycleStep = true; // we want the first toggle to yield false
         while (userSetNextFocus != null) {
             if (userSetNextFocus.isFocusable()
                     && userSetNextFocus.getVisibility() == View.VISIBLE
@@ -201,6 +203,14 @@
                 return userSetNextFocus;
             }
             userSetNextFocus = userSetNextFocus.findUserSetNextFocus(root, direction);
+            if (cycleStep = !cycleStep) {
+                cycleCheck = cycleCheck.findUserSetNextFocus(root, direction);
+                if (cycleCheck == userSetNextFocus) {
+                    // found a cycle, user-specified focus forms a loop and none of the views
+                    // are currently focusable.
+                    break;
+                }
+            }
         }
         return null;
     }
diff --git a/core/java/android/view/InputEventReceiver.java b/core/java/android/view/InputEventReceiver.java
index 20ab539..c566a65 100644
--- a/core/java/android/view/InputEventReceiver.java
+++ b/core/java/android/view/InputEventReceiver.java
@@ -111,9 +111,10 @@
      * to indicate whether the event was handled.  No new input events will be received
      * until {@link #finishInputEvent} is called.
      *
+     * @param displayId The display id on which input event triggered.
      * @param event The input event that was received.
      */
-    public void onInputEvent(InputEvent event) {
+    public void onInputEvent(InputEvent event, int displayId) {
         finishInputEvent(event, false);
     }
 
@@ -180,9 +181,9 @@
 
     // Called from native code.
     @SuppressWarnings("unused")
-    private void dispatchInputEvent(int seq, InputEvent event) {
+    private void dispatchInputEvent(int seq, InputEvent event, int displayId) {
         mSeqMap.put(event.getSequenceNumber(), seq);
-        onInputEvent(event);
+        onInputEvent(event, displayId);
     }
 
     // Called from native code.
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 22329f4..1a2968f 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -65,7 +65,8 @@
     private static native void nativeSetSize(long nativeObject, int w, int h);
     private static native void nativeSetTransparentRegionHint(long nativeObject, Region region);
     private static native void nativeSetAlpha(long nativeObject, float alpha);
-    private static native void nativeSetMatrix(long nativeObject, float dsdx, float dtdx, float dsdy, float dtdy);
+    private static native void nativeSetMatrix(long nativeObject, float dsdx, float dtdx,
+            float dtdy, float dsdy);
     private static native void nativeSetFlags(long nativeObject, int flags, int mask);
     private static native void nativeSetWindowCrop(long nativeObject, int l, int t, int r, int b);
     private static native void nativeSetFinalCrop(long nativeObject, int l, int t, int r, int b);
@@ -272,6 +273,15 @@
     public static final int POWER_MODE_DOZE_SUSPEND = 3;
 
     /**
+     * A value for windowType used to indicate that the window should be omitted from screenshots
+     * and display mirroring. A temporary workaround until we express such things with
+     * the hierarchy.
+     * TODO: b/64227542
+     * @hide
+     */
+    public static final int WINDOW_TYPE_DONT_SCREENSHOT = 441731;
+
+    /**
      * Create a surface with a name.
      * <p>
      * The surface creation flags specify what kind of surface to create and
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 6af01f66..eea692a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9711,6 +9711,7 @@
      * @param hasTransientState true if this view has transient state
      */
     public void setHasTransientState(boolean hasTransientState) {
+        final boolean oldHasTransientState = hasTransientState();
         mTransientStateCount = hasTransientState ? mTransientStateCount + 1 :
                 mTransientStateCount - 1;
         if (mTransientStateCount < 0) {
@@ -9722,9 +9723,10 @@
             // update flag if we've just incremented up from 0 or decremented down to 0
             mPrivateFlags2 = (mPrivateFlags2 & ~PFLAG2_HAS_TRANSIENT_STATE) |
                     (hasTransientState ? PFLAG2_HAS_TRANSIENT_STATE : 0);
-            if (mParent != null) {
+            final boolean newHasTransientState = hasTransientState();
+            if (mParent != null && newHasTransientState != oldHasTransientState) {
                 try {
-                    mParent.childHasTransientStateChanged(this, hasTransientState);
+                    mParent.childHasTransientStateChanged(this, newHasTransientState);
                 } catch (AbstractMethodError e) {
                     Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
                             " does not fully implement ViewParent", e);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 05f9da5..6bf4845 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -6752,7 +6752,7 @@
         }
 
         @Override
-        public void onInputEvent(InputEvent event) {
+        public void onInputEvent(InputEvent event, int displayId) {
             enqueueInputEvent(event, this, 0, true);
         }
 
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 4041bcf..1e50a85 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -992,21 +992,24 @@
         @Deprecated
         public static final int FLAG_TURN_SCREEN_ON = 0x00200000;
 
-        /** Window flag: when set the window will cause the keyguard to
-         * be dismissed, only if it is not a secure lock keyguard. Because such
-         * a keyguard is not needed for security, it will never re-appear if
-         * the user navigates to another window (in contrast to
-         * {@link #FLAG_SHOW_WHEN_LOCKED}, which will only temporarily
-         * hide both secure and non-secure keyguards but ensure they reappear
-         * when the user moves to another UI that doesn't hide them).
-         * If the keyguard is currently active and is secure (requires an
-         * unlock credential) than the user will still need to confirm it before
-         * seeing this window, unless {@link #FLAG_SHOW_WHEN_LOCKED} has
-         * also been set.
-         * @deprecated Use {@link #FLAG_SHOW_WHEN_LOCKED} or {@link KeyguardManager#dismissKeyguard}
-         * instead. Since keyguard was dismissed all the time as long as an activity with this flag
-         * on its window was focused, keyguard couldn't guard against unintentional touches on the
-         * screen, which isn't desired.
+        /**
+         * Window flag: when set the window will cause the keyguard to be
+         * dismissed, only if it is not a secure lock keyguard. Because such a
+         * keyguard is not needed for security, it will never re-appear if the
+         * user navigates to another window (in contrast to
+         * {@link #FLAG_SHOW_WHEN_LOCKED}, which will only temporarily hide both
+         * secure and non-secure keyguards but ensure they reappear when the
+         * user moves to another UI that doesn't hide them). If the keyguard is
+         * currently active and is secure (requires an unlock credential) than
+         * the user will still need to confirm it before seeing this window,
+         * unless {@link #FLAG_SHOW_WHEN_LOCKED} has also been set.
+         *
+         * @deprecated Use {@link #FLAG_SHOW_WHEN_LOCKED} or
+         *             {@link KeyguardManager#requestDismissKeyguard} instead.
+         *             Since keyguard was dismissed all the time as long as an
+         *             activity with this flag on its window was focused,
+         *             keyguard couldn't guard against unintentional touches on
+         *             the screen, which isn't desired.
          */
         @Deprecated
         public static final int FLAG_DISMISS_KEYGUARD = 0x00400000;
@@ -1413,11 +1416,12 @@
         public static final int PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 0x00080000;
 
         /**
-         * Flag to indicate that this window should be ignored when determining what parts of the
-         * screen can be magnified.
+         * Indicates that this window is the rounded corners overlay present on some
+         * devices this means that it will be excluded from: screenshots,
+         * screen magnification, and mirroring.
          * @hide
          */
-        public static final int PRIVATE_FLAG_NO_MAGNIFICATION_REGION_EFFECT = 0x00100000;
+        public static final int PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY = 0x00100000;
 
         /**
          * Control flags that are private to the platform.
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index 4c9cf40..98f8dc8 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -347,4 +347,11 @@
      * Requests the window manager to recompute the windows for accessibility.
      */
     public abstract void computeWindowsForAccessibility();
+
+    /**
+     * Called after virtual display Id is updated by
+     * {@link com.android.server.vr.Vr2dDisplay} with a specific
+     * {@param vr2dDisplayId}.
+     */
+    public abstract void setVr2dDisplayId(int vr2dDisplayId);
 }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 668d25e..49b7ed8 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
@@ -616,7 +617,16 @@
          * 2. motionEvent will be recycled after onPointerEvent returns so if it is needed later a
          * copy() must be made and the copy must be recycled.
          **/
-        public void onPointerEvent(MotionEvent motionEvent);
+        void onPointerEvent(MotionEvent motionEvent);
+
+        /**
+         * @see #onPointerEvent(MotionEvent)
+         **/
+        default void onPointerEvent(MotionEvent motionEvent, int displayId) {
+            if (displayId == DEFAULT_DISPLAY) {
+                onPointerEvent(motionEvent);
+            }
+        }
     }
 
     /** Window has been added to the screen. */
diff --git a/core/java/android/view/textclassifier/LangId.java b/core/java/android/view/textclassifier/LangId.java
deleted file mode 100644
index 23c7842..0000000
--- a/core/java/android/view/textclassifier/LangId.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2017 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.view.textclassifier;
-
-/**
- *  Java wrapper for LangId native library interface.
- *  This class is used to detect languages in text.
- */
-final class LangId {
-
-    static {
-        System.loadLibrary("textclassifier");
-    }
-
-    private final long mModelPtr;
-
-    /**
-     * Creates a new instance of LangId predictor, using the provided model image.
-     */
-    LangId(int fd) {
-        mModelPtr = nativeNew(fd);
-    }
-
-    /**
-     * Detects the language for given text.
-     */
-    public ClassificationResult[] findLanguages(String text) {
-        return nativeFindLanguages(mModelPtr, text);
-    }
-
-    /**
-     * Frees up the allocated memory.
-     */
-    public void close() {
-        nativeClose(mModelPtr);
-    }
-
-    private static native long nativeNew(int fd);
-
-    private static native ClassificationResult[] nativeFindLanguages(
-            long context, String text);
-
-    private static native void nativeClose(long context);
-
-    /** Classification result for findLanguage method. */
-    static final class ClassificationResult {
-        final String mLanguage;
-        /** float range: 0 - 1 */
-        final float mScore;
-
-        ClassificationResult(String language, float score) {
-            mLanguage = language;
-            mScore = score;
-        }
-    }
-}
diff --git a/core/java/android/view/textclassifier/TextClassificationManager.java b/core/java/android/view/textclassifier/TextClassificationManager.java
index efc88e2..d7b0776 100644
--- a/core/java/android/view/textclassifier/TextClassificationManager.java
+++ b/core/java/android/view/textclassifier/TextClassificationManager.java
@@ -16,37 +16,22 @@
 
 package android.view.textclassifier;
 
-import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemService;
 import android.content.Context;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
 
 import com.android.internal.util.Preconditions;
 
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.Locale;
-
 /**
  * Interface to the text classification service.
  */
 @SystemService(Context.TEXT_CLASSIFICATION_SERVICE)
 public final class TextClassificationManager {
 
-    private static final String LOG_TAG = "TextClassificationManager";
-
     private final Object mTextClassifierLock = new Object();
-    private final Object mLangIdLock = new Object();
 
     private final Context mContext;
-    private ParcelFileDescriptor mLangIdFd;
     private TextClassifier mTextClassifier;
-    private LangId mLangId;
 
     /** @hide */
     public TextClassificationManager(Context context) {
@@ -75,47 +60,4 @@
             mTextClassifier = textClassifier;
         }
     }
-
-    /**
-     * Returns information containing languages that were detected in the provided text.
-     * This is a blocking operation you should avoid calling it on the UI thread.
-     *
-     * @throws IllegalArgumentException if text is null
-     * @hide
-     */
-    public List<TextLanguage> detectLanguages(@NonNull CharSequence text) {
-        Preconditions.checkArgument(text != null);
-        try {
-            if (text.length() > 0) {
-                final LangId.ClassificationResult[] results =
-                        getLanguageDetector().findLanguages(text.toString());
-                final TextLanguage.Builder tlBuilder = new TextLanguage.Builder(0, text.length());
-                final int size = results.length;
-                for (int i = 0; i < size; i++) {
-                    tlBuilder.setLanguage(
-                            new Locale.Builder().setLanguageTag(results[i].mLanguage).build(),
-                            results[i].mScore);
-                }
-
-                return Collections.unmodifiableList(Arrays.asList(tlBuilder.build()));
-            }
-        } catch (Throwable t) {
-            // Avoid throwing from this method. Log the error.
-            Log.e(LOG_TAG, "Error detecting languages for text. Returning empty result.", t);
-        }
-        // Getting here means something went wrong. Return an empty result.
-        return Collections.emptyList();
-    }
-
-    private LangId getLanguageDetector() throws FileNotFoundException {
-        synchronized (mLangIdLock) {
-            if (mLangId == null) {
-                mLangIdFd = ParcelFileDescriptor.open(
-                        new File("/etc/textclassifier/textclassifier.langid.model"),
-                        ParcelFileDescriptor.MODE_READ_ONLY);
-                mLangId = new LangId(mLangIdFd.getFd());
-            }
-            return mLangId;
-        }
-    }
 }
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
deleted file mode 100644
index 209813a..0000000
--- a/core/java/android/view/textclassifier/TextLanguage.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2017 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.view.textclassifier;
-
-import android.annotation.FloatRange;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.List;
-import java.util.Locale;
-
-/**
- * Specifies detected languages for a section of text indicated by a start and end index.
- * @hide
- */
-public final class TextLanguage {
-
-    private final int mStartIndex;
-    private final int mEndIndex;
-    @NonNull private final EntityConfidence<Locale> mLanguageConfidence;
-    @NonNull private final List<Locale> mLanguages;
-
-    private TextLanguage(
-            int startIndex, int endIndex, @NonNull EntityConfidence<Locale> languageConfidence) {
-        mStartIndex = startIndex;
-        mEndIndex = endIndex;
-        mLanguageConfidence = new EntityConfidence<>(languageConfidence);
-        mLanguages = mLanguageConfidence.getEntities();
-    }
-
-    /**
-     * Returns the start index of the detected languages in the text provided to generate this
-     * object.
-     */
-    public int getStartIndex() {
-        return mStartIndex;
-    }
-
-    /**
-     * Returns the end index of the detected languages in the text provided to generate this object.
-     */
-    public int getEndIndex() {
-        return mEndIndex;
-    }
-
-    /**
-     * Returns the number of languages found in the classified text.
-     */
-    @IntRange(from = 0)
-    public int getLanguageCount() {
-        return mLanguages.size();
-    }
-
-    /**
-     * Returns the language locale at the specified index.
-     * Language locales are ordered from high confidence to low confidence.
-     *
-     * @throws IndexOutOfBoundsException if the specified index is out of range.
-     * @see #getLanguageCount() for the number of language locales available.
-     */
-    @NonNull
-    public Locale getLanguage(int index) {
-        return mLanguages.get(index);
-    }
-
-    /**
-     * Returns the confidence score for the specified language. The value ranges from
-     * 0 (low confidence) to 1 (high confidence). 0 indicates that the language was
-     * not found for the classified text.
-     */
-    @FloatRange(from = 0.0, to = 1.0)
-    public float getConfidenceScore(@Nullable Locale language) {
-        return mLanguageConfidence.getConfidenceScore(language);
-    }
-
-    @Override
-    public String toString() {
-        return String.format("TextLanguage {%d, %d, %s}",
-                mStartIndex, mEndIndex, mLanguageConfidence);
-    }
-
-    /**
-     * Builder to build {@link TextLanguage} objects.
-     */
-    public static final class Builder {
-
-        private final int mStartIndex;
-        private final int mEndIndex;
-        @NonNull private final EntityConfidence<Locale> mLanguageConfidence =
-                new EntityConfidence<>();
-
-        /**
-         * Creates a builder to build {@link TextLanguage} objects.
-         *
-         * @param startIndex the start index of the detected languages in the text provided
-         *      to generate the result
-         * @param endIndex the end index of the detected languages in the text provided
-         *      to generate the result. Must be greater than startIndex
-         */
-        public Builder(@IntRange(from = 0) int startIndex, @IntRange(from = 0) int endIndex) {
-            Preconditions.checkArgument(startIndex >= 0);
-            Preconditions.checkArgument(endIndex > startIndex);
-            mStartIndex = startIndex;
-            mEndIndex = endIndex;
-        }
-
-        /**
-         * Sets a language locale with the associated confidence score.
-         */
-        public Builder setLanguage(
-                @NonNull Locale locale, @FloatRange(from = 0.0, to = 1.0) float confidenceScore) {
-            mLanguageConfidence.setEntityType(locale, confidenceScore);
-            return this;
-        }
-
-        /**
-         * Builds and returns a {@link TextLanguage}.
-         */
-        public TextLanguage build() {
-            return new TextLanguage(mStartIndex, mEndIndex, mLanguageConfidence);
-        }
-    }
-}
diff --git a/core/java/android/webkit/SafeBrowsingResponse.java b/core/java/android/webkit/SafeBrowsingResponse.java
index 3540f80..024a70b 100644
--- a/core/java/android/webkit/SafeBrowsingResponse.java
+++ b/core/java/android/webkit/SafeBrowsingResponse.java
@@ -18,7 +18,7 @@
 
 /**
  * Used to indicate an action to take when hitting a malicious URL. Instances of this class are
- * created by the WebView and passed to {@link WebViewClient#onSafebrowsingHit}. The host
+ * created by the WebView and passed to {@link WebViewClient#onSafeBrowsingHit}. The host
  * application must call {@link #showInterstitial}, {@link #proceed}, or {@link #backToSafety} to
  * set the WebView's response to the Safe Browsing hit.
  */
diff --git a/core/java/android/widget/Button.java b/core/java/android/widget/Button.java
index 452ff17..634cbe3 100644
--- a/core/java/android/widget/Button.java
+++ b/core/java/android/widget/Button.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.util.AttributeSet;
+import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.PointerIcon;
 import android.widget.RemoteViews.RemoteView;
@@ -54,7 +55,7 @@
  *     }
  * }</pre>
  *
- * <p>The above snippet creates an instance of {@link View.OnClickListener} and wires
+ * <p>The above snippet creates an instance of {@link android.view.View.OnClickListener} and wires
  * the listener to the button using
  * {@link #setOnClickListener setOnClickListener(View.OnClickListener)}.
  * As a result, the system executes the code you write in {@code onClick(View)} after the
@@ -77,15 +78,8 @@
  * {@link android.R.styleable#Button Button Attributes},
  * {@link android.R.styleable#TextView TextView Attributes},
  * {@link android.R.styleable#View View Attributes}.  See the
- * {@link <a href="{@docRoot}guide/topics/ui/themes.html#ApplyingStyles">Styles and Themes</a>
+ * <a href="{@docRoot}guide/topics/ui/themes.html#ApplyingStyles">Styles and Themes</a>
  * guide to learn how to implement and organize overrides to style-related attributes.</p>
- *
- * @see
- * <a href="{@docRoot}guide/topics/ui/controls/button.html">Buttons Guide</a>
- * {@link android.R.styleable#Button Styleable Button Attributes},
- * {@link android.R.styleable#TextView Styleable TextView Attributes},
- * {@link android.R.styleable#View Styleable View Attributes},
- *
  */
 @RemoteView
 public class Button extends TextView {
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 02ead04..7aec44c 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1916,7 +1916,7 @@
         final int offset = mTextView.getSelectionStart();
         final int line = layout.getLineForOffset(offset);
         final int top = layout.getLineTop(line);
-        final int bottom = layout.getLineTop(line + 1);
+        final int bottom = layout.getLineBottomWithoutSpacing(line);
 
         final boolean clamped = layout.shouldClampCursor(line);
         updateCursorPosition(top, bottom, layout.getPrimaryHorizontal(offset, clamped));
@@ -2972,7 +2972,8 @@
 
         @Override
         protected int getVerticalLocalPosition(int line) {
-            return mTextView.getLayout().getLineBottom(line);
+            final Layout layout = mTextView.getLayout();
+            return layout.getLineBottomWithoutSpacing(line);
         }
 
         @Override
@@ -3629,7 +3630,8 @@
 
         @Override
         protected int getVerticalLocalPosition(int line) {
-            return mTextView.getLayout().getLineBottom(line) - mContainerMarginTop;
+            final Layout layout = mTextView.getLayout();
+            return layout.getLineBottomWithoutSpacing(line) - mContainerMarginTop;
         }
 
         @Override
@@ -4008,7 +4010,7 @@
                         primaryHorizontal,
                         layout.getLineTop(line),
                         primaryHorizontal,
-                        layout.getLineTop(line + 1) + mHandleHeight);
+                        layout.getLineBottom(line) - layout.getLineBottom(line) + mHandleHeight);
             }
             // Take TextView's padding and scroll into account.
             int textHorizontalOffset = mTextView.viewportToContentHorizontalOffset();
@@ -4103,7 +4105,7 @@
                         + viewportToContentVerticalOffset;
                 final float insertionMarkerBaseline = layout.getLineBaseline(line)
                         + viewportToContentVerticalOffset;
-                final float insertionMarkerBottom = layout.getLineBottom(line)
+                final float insertionMarkerBottom = layout.getLineBottomWithoutSpacing(line)
                         + viewportToContentVerticalOffset;
                 final boolean isTopVisible = mTextView
                         .isPositionVisible(insertionMarkerX, insertionMarkerTop);
@@ -4371,7 +4373,7 @@
 
                 mPositionX = getCursorHorizontalPosition(layout, offset) - mHotspotX
                         - getHorizontalOffset() + getCursorOffset();
-                mPositionY = layout.getLineBottom(line);
+                mPositionY = layout.getLineBottomWithoutSpacing(line);
 
                 // Take TextView's padding and scroll into account.
                 mPositionX += mTextView.viewportToContentHorizontalOffset();
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index 88c3c5b..380bf7a 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -66,7 +66,7 @@
  * both horizontal and vertical alignment of all child views within the single row or column.</p>
  *
  * <p>You can set
- * {@link LinearLayout.LayoutParams.html#attr_android:layout_weight android:layout_weight}
+ * {@link android.R.styleable#LinearLayout_Layout_layout_weight android:layout_weight}
  * on individual child views to specify how linear layout divides remaining space amongst
  * the views it contains. See the
  * <a href="https://developer.android.com/guide/topics/ui/layout/linear.html">Linear Layout</a>
diff --git a/core/java/android/widget/SimpleMonthView.java b/core/java/android/widget/SimpleMonthView.java
index 855d1d2..9982732 100644
--- a/core/java/android/widget/SimpleMonthView.java
+++ b/core/java/android/widget/SimpleMonthView.java
@@ -517,6 +517,8 @@
     private int findClosestRow(@Nullable Rect previouslyFocusedRect) {
         if (previouslyFocusedRect == null) {
             return 3;
+        } else if (mDayHeight == 0) {
+            return 0; // There hasn't been a layout, so just choose the first row
         } else {
             int centerY = previouslyFocusedRect.centerY();
 
@@ -545,6 +547,8 @@
     private int findClosestColumn(@Nullable Rect previouslyFocusedRect) {
         if (previouslyFocusedRect == null) {
             return DAYS_IN_WEEK / 2;
+        } else if (mCellWidth == 0) {
+            return 0; // There hasn't been a layout, so we can just choose the first column
         } else {
             int centerX = previouslyFocusedRect.centerX() - mPaddingLeft;
             final int columnFromLeft =
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 4c7039b..bbfa6e0 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -119,6 +119,7 @@
 import android.util.DisplayMetrics;
 import android.util.IntArray;
 import android.util.Log;
+import android.util.SparseIntArray;
 import android.util.TypedValue;
 import android.view.AccessibilityIterators.TextSegmentIterator;
 import android.view.ActionMode;
@@ -855,22 +856,9 @@
 
         mTransformation = null;
 
-        int textColorHighlight = 0;
-        ColorStateList textColor = null;
-        ColorStateList textColorHint = null;
-        ColorStateList textColorLink = null;
-        int textSize = 15;
-        String fontFamily = null;
-        Typeface fontTypeface = null;
-        boolean fontFamilyExplicit = false;
-        int typefaceIndex = -1;
-        int styleIndex = -1;
-        boolean allCaps = false;
-        int shadowcolor = 0;
-        float dx = 0, dy = 0, r = 0;
-        boolean elegant = false;
-        float letterSpacing = 0;
-        String fontFeatureSettings = null;
+        final TextAppearanceAttributes attributes = new TextAppearanceAttributes();
+        attributes.mTextColor = ColorStateList.valueOf(0xFF000000);
+        attributes.mTextSize = 15;
         mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
         mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
         mJustificationMode = Layout.JUSTIFICATION_MODE_NONE;
@@ -894,87 +882,8 @@
                     ap, com.android.internal.R.styleable.TextAppearance);
         }
         if (appearance != null) {
-            int n = appearance.getIndexCount();
-            for (int i = 0; i < n; i++) {
-                int attr = appearance.getIndex(i);
-
-                switch (attr) {
-                    case com.android.internal.R.styleable.TextAppearance_textColorHighlight:
-                        textColorHighlight = appearance.getColor(attr, textColorHighlight);
-                        break;
-
-                    case com.android.internal.R.styleable.TextAppearance_textColor:
-                        textColor = appearance.getColorStateList(attr);
-                        break;
-
-                    case com.android.internal.R.styleable.TextAppearance_textColorHint:
-                        textColorHint = appearance.getColorStateList(attr);
-                        break;
-
-                    case com.android.internal.R.styleable.TextAppearance_textColorLink:
-                        textColorLink = appearance.getColorStateList(attr);
-                        break;
-
-                    case com.android.internal.R.styleable.TextAppearance_textSize:
-                        textSize = appearance.getDimensionPixelSize(attr, textSize);
-                        break;
-
-                    case com.android.internal.R.styleable.TextAppearance_typeface:
-                        typefaceIndex = appearance.getInt(attr, -1);
-                        break;
-
-                    case com.android.internal.R.styleable.TextAppearance_fontFamily:
-                        if (!context.isRestricted() && context.canLoadUnsafeResources()) {
-                            try {
-                                fontTypeface = appearance.getFont(attr);
-                            } catch (UnsupportedOperationException
-                                    | Resources.NotFoundException e) {
-                                // Expected if it is not a font resource.
-                            }
-                        }
-                        if (fontTypeface == null) {
-                            fontFamily = appearance.getString(attr);
-                        }
-                        break;
-
-                    case com.android.internal.R.styleable.TextAppearance_textStyle:
-                        styleIndex = appearance.getInt(attr, -1);
-                        break;
-
-                    case com.android.internal.R.styleable.TextAppearance_textAllCaps:
-                        allCaps = appearance.getBoolean(attr, false);
-                        break;
-
-                    case com.android.internal.R.styleable.TextAppearance_shadowColor:
-                        shadowcolor = appearance.getInt(attr, 0);
-                        break;
-
-                    case com.android.internal.R.styleable.TextAppearance_shadowDx:
-                        dx = appearance.getFloat(attr, 0);
-                        break;
-
-                    case com.android.internal.R.styleable.TextAppearance_shadowDy:
-                        dy = appearance.getFloat(attr, 0);
-                        break;
-
-                    case com.android.internal.R.styleable.TextAppearance_shadowRadius:
-                        r = appearance.getFloat(attr, 0);
-                        break;
-
-                    case com.android.internal.R.styleable.TextAppearance_elegantTextHeight:
-                        elegant = appearance.getBoolean(attr, false);
-                        break;
-
-                    case com.android.internal.R.styleable.TextAppearance_letterSpacing:
-                        letterSpacing = appearance.getFloat(attr, 0);
-                        break;
-
-                    case com.android.internal.R.styleable.TextAppearance_fontFeatureSettings:
-                        fontFeatureSettings = appearance.getString(attr);
-                        break;
-                }
-            }
-
+            readTextAppearance(context, appearance, attributes, false /* styleArray */);
+            attributes.mFontFamilyExplicit = false;
             appearance.recycle();
         }
 
@@ -1005,6 +914,8 @@
         a = theme.obtainStyledAttributes(
                     attrs, com.android.internal.R.styleable.TextView, defStyleAttr, defStyleRes);
 
+        readTextAppearance(context, a, attributes, true /* styleArray */);
+
         int n = a.getIndexCount();
 
         boolean fromResourceId = false;
@@ -1195,69 +1106,10 @@
                     mFreezesText = a.getBoolean(attr, false);
                     break;
 
-                case com.android.internal.R.styleable.TextView_shadowColor:
-                    shadowcolor = a.getInt(attr, 0);
-                    break;
-
-                case com.android.internal.R.styleable.TextView_shadowDx:
-                    dx = a.getFloat(attr, 0);
-                    break;
-
-                case com.android.internal.R.styleable.TextView_shadowDy:
-                    dy = a.getFloat(attr, 0);
-                    break;
-
-                case com.android.internal.R.styleable.TextView_shadowRadius:
-                    r = a.getFloat(attr, 0);
-                    break;
-
                 case com.android.internal.R.styleable.TextView_enabled:
                     setEnabled(a.getBoolean(attr, isEnabled()));
                     break;
 
-                case com.android.internal.R.styleable.TextView_textColorHighlight:
-                    textColorHighlight = a.getColor(attr, textColorHighlight);
-                    break;
-
-                case com.android.internal.R.styleable.TextView_textColor:
-                    textColor = a.getColorStateList(attr);
-                    break;
-
-                case com.android.internal.R.styleable.TextView_textColorHint:
-                    textColorHint = a.getColorStateList(attr);
-                    break;
-
-                case com.android.internal.R.styleable.TextView_textColorLink:
-                    textColorLink = a.getColorStateList(attr);
-                    break;
-
-                case com.android.internal.R.styleable.TextView_textSize:
-                    textSize = a.getDimensionPixelSize(attr, textSize);
-                    break;
-
-                case com.android.internal.R.styleable.TextView_typeface:
-                    typefaceIndex = a.getInt(attr, typefaceIndex);
-                    break;
-
-                case com.android.internal.R.styleable.TextView_textStyle:
-                    styleIndex = a.getInt(attr, styleIndex);
-                    break;
-
-                case com.android.internal.R.styleable.TextView_fontFamily:
-                    if (!context.isRestricted() && context.canLoadUnsafeResources()) {
-                        try {
-                            fontTypeface = a.getFont(attr);
-                        } catch (UnsupportedOperationException | Resources.NotFoundException e) {
-                            // Expected if it is not a resource reference or if it is a reference to
-                            // another resource type.
-                        }
-                    }
-                    if (fontTypeface == null) {
-                        fontFamily = a.getString(attr);
-                    }
-                    fontFamilyExplicit = true;
-                    break;
-
                 case com.android.internal.R.styleable.TextView_password:
                     password = a.getBoolean(attr, password);
                     break;
@@ -1345,22 +1197,6 @@
                     setTextIsSelectable(a.getBoolean(attr, false));
                     break;
 
-                case com.android.internal.R.styleable.TextView_textAllCaps:
-                    allCaps = a.getBoolean(attr, false);
-                    break;
-
-                case com.android.internal.R.styleable.TextView_elegantTextHeight:
-                    elegant = a.getBoolean(attr, false);
-                    break;
-
-                case com.android.internal.R.styleable.TextView_letterSpacing:
-                    letterSpacing = a.getFloat(attr, 0);
-                    break;
-
-                case com.android.internal.R.styleable.TextView_fontFeatureSettings:
-                    fontFeatureSettings = a.getString(attr);
-                    break;
-
                 case com.android.internal.R.styleable.TextView_breakStrategy:
                     mBreakStrategy = a.getInt(attr, Layout.BREAK_STRATEGY_SIMPLE);
                     break;
@@ -1592,38 +1428,20 @@
                 break;
         }
 
-        setTextColor(textColor != null ? textColor : ColorStateList.valueOf(0xFF000000));
-        setHintTextColor(textColorHint);
-        setLinkTextColor(textColorLink);
-        if (textColorHighlight != 0) {
-            setHighlightColor(textColorHighlight);
-        }
-        setRawTextSize(textSize, true /* shouldRequestLayout */);
-        setElegantTextHeight(elegant);
-        setLetterSpacing(letterSpacing);
-        setFontFeatureSettings(fontFeatureSettings);
-
-        if (allCaps) {
-            setTransformationMethod(new AllCapsTransformationMethod(getContext()));
-        }
-
-        if (password || passwordInputType || webPasswordInputType || numberPasswordInputType) {
-            setTransformationMethod(PasswordTransformationMethod.getInstance());
-            typefaceIndex = MONOSPACE;
-        } else if (mEditor != null
+        final boolean isPassword = password || passwordInputType || webPasswordInputType
+                || numberPasswordInputType;
+        final boolean isMonospaceEnforced = isPassword || (mEditor != null
                 && (mEditor.mInputType
-                        & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION))
-                == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD)) {
-            typefaceIndex = MONOSPACE;
+                & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION))
+                == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD));
+        if (isMonospaceEnforced) {
+            attributes.mTypefaceIndex = MONOSPACE;
         }
 
-        if (typefaceIndex != -1 && !fontFamilyExplicit) {
-            fontFamily = null;
-        }
-        setTypefaceFromAttrs(fontTypeface, fontFamily, typefaceIndex, styleIndex);
+        applyTextAppearance(attributes);
 
-        if (shadowcolor != 0) {
-            setShadowLayer(r, dx, dy, shadowcolor);
+        if (isPassword) {
+            setTransformationMethod(PasswordTransformationMethod.getInstance());
         }
 
         if (maxlength >= 0) {
@@ -3397,79 +3215,244 @@
     @Deprecated
     public void setTextAppearance(Context context, @StyleRes int resId) {
         final TypedArray ta = context.obtainStyledAttributes(resId, R.styleable.TextAppearance);
+        final TextAppearanceAttributes attributes = new TextAppearanceAttributes();
+        readTextAppearance(context, ta, attributes, false /* styleArray */);
+        ta.recycle();
+        applyTextAppearance(attributes);
+    }
 
-        final int textColorHighlight = ta.getColor(
-                R.styleable.TextAppearance_textColorHighlight, 0);
-        if (textColorHighlight != 0) {
-            setHighlightColor(textColorHighlight);
+    /**
+     * Set of attributes that can be defined in a Text Appearance. This is used to simplify the code
+     * that reads these attributes in the constructor and in {@link #setTextAppearance}.
+     */
+    private static class TextAppearanceAttributes {
+        int mTextColorHighlight = 0;
+        ColorStateList mTextColor = null;
+        ColorStateList mTextColorHint = null;
+        ColorStateList mTextColorLink = null;
+        int mTextSize = 0;
+        String mFontFamily = null;
+        Typeface mFontTypeface = null;
+        boolean mFontFamilyExplicit = false;
+        int mTypefaceIndex = -1;
+        int mStyleIndex = -1;
+        boolean mAllCaps = false;
+        int mShadowColor = 0;
+        float mShadowDx = 0, mShadowDy = 0, mShadowRadius = 0;
+        boolean mHasElegant = false;
+        boolean mElegant = false;
+        boolean mHasLetterSpacing = false;
+        float mLetterSpacing = 0;
+        String mFontFeatureSettings = null;
+
+        @Override
+        public String toString() {
+            return "TextAppearanceAttributes {\n"
+                    + "    mTextColorHighlight:" + mTextColorHighlight + "\n"
+                    + "    mTextColor:" + mTextColor + "\n"
+                    + "    mTextColorHint:" + mTextColorHint + "\n"
+                    + "    mTextColorLink:" + mTextColorLink + "\n"
+                    + "    mTextSize:" + mTextSize + "\n"
+                    + "    mFontFamily:" + mFontFamily + "\n"
+                    + "    mFontTypeface:" + mFontTypeface + "\n"
+                    + "    mFontFamilyExplicit:" + mFontFamilyExplicit + "\n"
+                    + "    mTypefaceIndex:" + mTypefaceIndex + "\n"
+                    + "    mStyleIndex:" + mStyleIndex + "\n"
+                    + "    mAllCaps:" + mAllCaps + "\n"
+                    + "    mShadowColor:" + mShadowColor + "\n"
+                    + "    mShadowDx:" + mShadowDx + "\n"
+                    + "    mShadowDy:" + mShadowDy + "\n"
+                    + "    mShadowRadius:" + mShadowRadius + "\n"
+                    + "    mHasElegant:" + mHasElegant + "\n"
+                    + "    mElegant:" + mElegant + "\n"
+                    + "    mHasLetterSpacing:" + mHasLetterSpacing + "\n"
+                    + "    mLetterSpacing:" + mLetterSpacing + "\n"
+                    + "    mFontFeatureSettings:" + mFontFeatureSettings + "\n"
+                    + "}";
         }
+    }
 
-        final ColorStateList textColor = ta.getColorStateList(R.styleable.TextAppearance_textColor);
-        if (textColor != null) {
-            setTextColor(textColor);
-        }
+    // Maps styleable attributes that exist both in TextView style and TextAppearance.
+    private static final SparseIntArray sAppearanceValues = new SparseIntArray();
+    static {
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_textColorHighlight,
+                com.android.internal.R.styleable.TextAppearance_textColorHighlight);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_textColor,
+                com.android.internal.R.styleable.TextAppearance_textColor);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_textColorHint,
+                com.android.internal.R.styleable.TextAppearance_textColorHint);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_textColorLink,
+                com.android.internal.R.styleable.TextAppearance_textColorLink);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_textSize,
+                com.android.internal.R.styleable.TextAppearance_textSize);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_typeface,
+                com.android.internal.R.styleable.TextAppearance_typeface);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_fontFamily,
+                com.android.internal.R.styleable.TextAppearance_fontFamily);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_textStyle,
+                com.android.internal.R.styleable.TextAppearance_textStyle);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_textAllCaps,
+                com.android.internal.R.styleable.TextAppearance_textAllCaps);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_shadowColor,
+                com.android.internal.R.styleable.TextAppearance_shadowColor);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_shadowDx,
+                com.android.internal.R.styleable.TextAppearance_shadowDx);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_shadowDy,
+                com.android.internal.R.styleable.TextAppearance_shadowDy);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_shadowRadius,
+                com.android.internal.R.styleable.TextAppearance_shadowRadius);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_elegantTextHeight,
+                com.android.internal.R.styleable.TextAppearance_elegantTextHeight);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_letterSpacing,
+                com.android.internal.R.styleable.TextAppearance_letterSpacing);
+        sAppearanceValues.put(com.android.internal.R.styleable.TextView_fontFeatureSettings,
+                com.android.internal.R.styleable.TextAppearance_fontFeatureSettings);
+    }
 
-        final int textSize = ta.getDimensionPixelSize(R.styleable.TextAppearance_textSize, 0);
-        if (textSize != 0) {
-            setRawTextSize(textSize, true /* shouldRequestLayout */);
-        }
-
-        final ColorStateList textColorHint = ta.getColorStateList(
-                R.styleable.TextAppearance_textColorHint);
-        if (textColorHint != null) {
-            setHintTextColor(textColorHint);
-        }
-
-        final ColorStateList textColorLink = ta.getColorStateList(
-                R.styleable.TextAppearance_textColorLink);
-        if (textColorLink != null) {
-            setLinkTextColor(textColorLink);
-        }
-
-        Typeface fontTypeface = null;
-        String fontFamily = null;
-        if (!context.isRestricted() && context.canLoadUnsafeResources()) {
-            try {
-                fontTypeface = ta.getFont(R.styleable.TextAppearance_fontFamily);
-            } catch (UnsupportedOperationException | Resources.NotFoundException e) {
-                // Expected if it is not a font resource.
+    /**
+     * Read the Text Appearance attributes from a given TypedArray and set its values to the given
+     * set. If the TypedArray contains a value that was already set in the given attributes, that
+     * will be overriden.
+     *
+     * @param context The Context to be used
+     * @param appearance The TypedArray to read properties from
+     * @param attributes the TextAppearanceAttributes to fill in
+     * @param styleArray Whether the given TypedArray is a style or a TextAppearance. This defines
+     *                   what attribute indexes will be used to read the properties.
+     */
+    private void readTextAppearance(Context context, TypedArray appearance,
+            TextAppearanceAttributes attributes, boolean styleArray) {
+        final int n = appearance.getIndexCount();
+        for (int i = 0; i < n; i++) {
+            final int attr = appearance.getIndex(i);
+            int index = attr;
+            // Translate style array index ids to TextAppearance ids.
+            if (styleArray) {
+                index = sAppearanceValues.get(attr, -1);
+                if (index == -1) {
+                    // This value is not part of a Text Appearance and should be ignored.
+                    continue;
+                }
+            }
+            switch (index) {
+                case com.android.internal.R.styleable.TextAppearance_textColorHighlight:
+                    attributes.mTextColorHighlight =
+                            appearance.getColor(attr, attributes.mTextColorHighlight);
+                    break;
+                case com.android.internal.R.styleable.TextAppearance_textColor:
+                    attributes.mTextColor = appearance.getColorStateList(attr);
+                    break;
+                case com.android.internal.R.styleable.TextAppearance_textColorHint:
+                    attributes.mTextColorHint = appearance.getColorStateList(attr);
+                    break;
+                case com.android.internal.R.styleable.TextAppearance_textColorLink:
+                    attributes.mTextColorLink = appearance.getColorStateList(attr);
+                    break;
+                case com.android.internal.R.styleable.TextAppearance_textSize:
+                    attributes.mTextSize =
+                            appearance.getDimensionPixelSize(attr, attributes.mTextSize);
+                    break;
+                case com.android.internal.R.styleable.TextAppearance_typeface:
+                    attributes.mTypefaceIndex = appearance.getInt(attr, attributes.mTypefaceIndex);
+                    if (attributes.mTypefaceIndex != -1 && !attributes.mFontFamilyExplicit) {
+                        attributes.mFontFamily = null;
+                    }
+                    break;
+                case com.android.internal.R.styleable.TextAppearance_fontFamily:
+                    if (!context.isRestricted() && context.canLoadUnsafeResources()) {
+                        try {
+                            attributes.mFontTypeface = appearance.getFont(attr);
+                        } catch (UnsupportedOperationException | Resources.NotFoundException e) {
+                            // Expected if it is not a font resource.
+                        }
+                    }
+                    if (attributes.mFontTypeface == null) {
+                        attributes.mFontFamily = appearance.getString(attr);
+                    }
+                    attributes.mFontFamilyExplicit = true;
+                    break;
+                case com.android.internal.R.styleable.TextAppearance_textStyle:
+                    attributes.mStyleIndex = appearance.getInt(attr, attributes.mStyleIndex);
+                    break;
+                case com.android.internal.R.styleable.TextAppearance_textAllCaps:
+                    attributes.mAllCaps = appearance.getBoolean(attr, attributes.mAllCaps);
+                    break;
+                case com.android.internal.R.styleable.TextAppearance_shadowColor:
+                    attributes.mShadowColor = appearance.getInt(attr, attributes.mShadowColor);
+                    break;
+                case com.android.internal.R.styleable.TextAppearance_shadowDx:
+                    attributes.mShadowDx = appearance.getFloat(attr, attributes.mShadowDx);
+                    break;
+                case com.android.internal.R.styleable.TextAppearance_shadowDy:
+                    attributes.mShadowDy = appearance.getFloat(attr, attributes.mShadowDy);
+                    break;
+                case com.android.internal.R.styleable.TextAppearance_shadowRadius:
+                    attributes.mShadowRadius = appearance.getFloat(attr, attributes.mShadowRadius);
+                    break;
+                case com.android.internal.R.styleable.TextAppearance_elegantTextHeight:
+                    attributes.mHasElegant = true;
+                    attributes.mElegant = appearance.getBoolean(attr, attributes.mElegant);
+                    break;
+                case com.android.internal.R.styleable.TextAppearance_letterSpacing:
+                    attributes.mHasLetterSpacing = true;
+                    attributes.mLetterSpacing =
+                            appearance.getFloat(attr, attributes.mLetterSpacing);
+                    break;
+                case com.android.internal.R.styleable.TextAppearance_fontFeatureSettings:
+                    attributes.mFontFeatureSettings = appearance.getString(attr);
+                    break;
+                default:
             }
         }
-        if (fontTypeface == null) {
-            fontFamily = ta.getString(R.styleable.TextAppearance_fontFamily);
-        }
-        final int typefaceIndex = ta.getInt(R.styleable.TextAppearance_typeface, -1);
-        final int styleIndex = ta.getInt(R.styleable.TextAppearance_textStyle, -1);
-        setTypefaceFromAttrs(fontTypeface, fontFamily, typefaceIndex, styleIndex);
+    }
 
-        final int shadowColor = ta.getInt(R.styleable.TextAppearance_shadowColor, 0);
-        if (shadowColor != 0) {
-            final float dx = ta.getFloat(R.styleable.TextAppearance_shadowDx, 0);
-            final float dy = ta.getFloat(R.styleable.TextAppearance_shadowDy, 0);
-            final float r = ta.getFloat(R.styleable.TextAppearance_shadowRadius, 0);
-            setShadowLayer(r, dx, dy, shadowColor);
+    private void applyTextAppearance(TextAppearanceAttributes attributes) {
+        if (attributes.mTextColor != null) {
+            setTextColor(attributes.mTextColor);
         }
 
-        if (ta.getBoolean(R.styleable.TextAppearance_textAllCaps, false)) {
+        if (attributes.mTextColorHint != null) {
+            setHintTextColor(attributes.mTextColorHint);
+        }
+
+        if (attributes.mTextColorLink != null) {
+            setLinkTextColor(attributes.mTextColorLink);
+        }
+
+        if (attributes.mTextColorHighlight != 0) {
+            setHighlightColor(attributes.mTextColorHighlight);
+        }
+
+        if (attributes.mTextSize != 0) {
+            setRawTextSize(attributes.mTextSize, true /* shouldRequestLayout */);
+        }
+
+        if (attributes.mTypefaceIndex != -1 && !attributes.mFontFamilyExplicit) {
+            attributes.mFontFamily = null;
+        }
+        setTypefaceFromAttrs(attributes.mFontTypeface, attributes.mFontFamily,
+                attributes.mTypefaceIndex, attributes.mStyleIndex);
+
+        if (attributes.mShadowColor != 0) {
+            setShadowLayer(attributes.mShadowRadius, attributes.mShadowDx, attributes.mShadowDy,
+                    attributes.mShadowColor);
+        }
+
+        if (attributes.mAllCaps) {
             setTransformationMethod(new AllCapsTransformationMethod(getContext()));
         }
 
-        if (ta.hasValue(R.styleable.TextAppearance_elegantTextHeight)) {
-            setElegantTextHeight(ta.getBoolean(
-                    R.styleable.TextAppearance_elegantTextHeight, false));
+        if (attributes.mHasElegant) {
+            setElegantTextHeight(attributes.mElegant);
         }
 
-        if (ta.hasValue(R.styleable.TextAppearance_letterSpacing)) {
-            setLetterSpacing(ta.getFloat(
-                    R.styleable.TextAppearance_letterSpacing, 0));
+        if (attributes.mHasLetterSpacing) {
+            setLetterSpacing(attributes.mLetterSpacing);
         }
 
-        if (ta.hasValue(R.styleable.TextAppearance_fontFeatureSettings)) {
-            setFontFeatureSettings(ta.getString(
-                    R.styleable.TextAppearance_fontFeatureSettings));
+        if (attributes.mFontFeatureSettings != null) {
+            setFontFeatureSettings(attributes.mFontFeatureSettings);
         }
-
-        ta.recycle();
     }
 
     /**
@@ -3744,6 +3727,8 @@
      *
      * @param elegant set the paint's elegant metrics flag.
      *
+     * @see Paint#isElegantTextHeight(boolean)
+     *
      * @attr ref android.R.styleable#TextView_elegantTextHeight
      */
     public void setElegantTextHeight(boolean elegant) {
@@ -3758,6 +3743,19 @@
     }
 
     /**
+     * Get the value of the TextView's elegant height metrics flag. This setting selects font
+     * variants that have not been compacted to fit Latin-based vertical
+     * metrics, and also increases top and bottom bounds to provide more space.
+     * @return {@code true} if the elegant height metrics flag is set.
+     *
+     * @see #setElegantTextHeight(boolean)
+     * @see Paint#setElegantTextHeight(boolean)
+     */
+    public boolean isElegantTextHeight() {
+        return mTextPaint.isElegantTextHeight();
+    }
+
+    /**
      * Gets the text letter-space value, which determines the spacing between characters.
      * The value returned is in ems. Normally, this value is 0.0.
      * @return The text letter-space value in ems.
@@ -4946,20 +4944,21 @@
 
     private void updateTextColors() {
         boolean inval = false;
-        int color = mTextColor.getColorForState(getDrawableState(), 0);
+        final int[] drawableState = getDrawableState();
+        int color = mTextColor.getColorForState(drawableState, 0);
         if (color != mCurTextColor) {
             mCurTextColor = color;
             inval = true;
         }
         if (mLinkTextColor != null) {
-            color = mLinkTextColor.getColorForState(getDrawableState(), 0);
+            color = mLinkTextColor.getColorForState(drawableState, 0);
             if (color != mTextPaint.linkColor) {
                 mTextPaint.linkColor = color;
                 inval = true;
             }
         }
         if (mHintTextColor != null) {
-            color = mHintTextColor.getColorForState(getDrawableState(), 0);
+            color = mHintTextColor.getColorForState(drawableState, 0);
             if (color != mCurHintTextColor) {
                 mCurHintTextColor = color;
                 if (mText.length() == 0) {
@@ -8117,6 +8116,8 @@
 
         int des = -1;
         boolean fromexisting = false;
+        final float widthLimit = (widthMode == MeasureSpec.AT_MOST)
+                ?  (float) widthSize : Float.MAX_VALUE;
 
         if (widthMode == MeasureSpec.EXACTLY) {
             // Parent has told us how big to be. So be it.
@@ -8137,8 +8138,8 @@
 
             if (boring == null || boring == UNKNOWN_BORING) {
                 if (des < 0) {
-                    des = (int) Math.ceil(Layout.getDesiredWidth(mTransformed, 0,
-                            mTransformed.length(), mTextPaint, mTextDir));
+                    des = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mTransformed, 0,
+                            mTransformed.length(), mTextPaint, mTextDir, widthLimit));
                 }
                 width = des;
             } else {
@@ -8168,8 +8169,8 @@
 
                 if (hintBoring == null || hintBoring == UNKNOWN_BORING) {
                     if (hintDes < 0) {
-                        hintDes = (int) Math.ceil(Layout.getDesiredWidth(mHint, 0, mHint.length(),
-                                mTextPaint, mTextDir));
+                        hintDes = (int) Math.ceil(Layout.getDesiredWidthWithLimit(mHint, 0,
+                                mHint.length(), mTextPaint, mTextDir, widthLimit));
                     }
                     hintWidth = hintDes;
                 } else {
@@ -8346,7 +8347,9 @@
     }
 
     private boolean suggestedSizeFitsInSpace(int suggestedSizeInPx, RectF availableSpace) {
-        final CharSequence text = getText();
+        final CharSequence text = mTransformed != null
+                ? mTransformed
+                : getText();
         final int maxLines = getMaxLines();
         if (mTempTextPaint == null) {
             mTempTextPaint = new TextPaint();
@@ -9020,6 +9023,20 @@
     }
 
     /**
+     *
+     * Checks whether the transformation method applied to this TextView is set to ALL CAPS. This
+     * settings is internally ignored if this field is editable or selectable.
+     * @return Whether the current transformation method is for ALL CAPS.
+     *
+     * @see #setAllCaps(boolean)
+     * @see #setTransformationMethod(TransformationMethod)
+     */
+    public boolean isAllCaps() {
+        final TransformationMethod method = getTransformationMethod();
+        return method != null && method instanceof AllCapsTransformationMethod;
+    }
+
+    /**
      * If true, sets the properties of this field (number of lines, horizontally scrolling,
      * transformation method) to be for a single-line input; if false, restores these to the default
      * conditions.
diff --git a/core/java/com/android/internal/annotations/VisibleForTesting.java b/core/java/com/android/internal/annotations/VisibleForTesting.java
index bc3121c..99512ac6 100644
--- a/core/java/com/android/internal/annotations/VisibleForTesting.java
+++ b/core/java/com/android/internal/annotations/VisibleForTesting.java
@@ -27,7 +27,7 @@
  * visibility should have been if it had not been made public or package-private for testing.
  * The default is to consider the element private.
  */
-@Retention(RetentionPolicy.SOURCE)
+@Retention(RetentionPolicy.CLASS)
 public @interface VisibleForTesting {
     /**
      * Intended visibility if the element had not been made public or package-private for
diff --git a/core/java/com/android/internal/colorextraction/ColorExtractor.java b/core/java/com/android/internal/colorextraction/ColorExtractor.java
index 727412b..ef98a5e 100644
--- a/core/java/com/android/internal/colorextraction/ColorExtractor.java
+++ b/core/java/com/android/internal/colorextraction/ColorExtractor.java
@@ -22,6 +22,7 @@
 import android.app.WallpaperManager;
 import android.content.Context;
 import android.os.Trace;
+import android.os.UserHandle;
 import android.util.Log;
 import android.util.SparseArray;
 
@@ -44,6 +45,7 @@
     private static final int[] sGradientTypes = new int[]{TYPE_NORMAL, TYPE_DARK, TYPE_EXTRA_DARK};
 
     private static final String TAG = "ColorExtractor";
+    private static final boolean DEBUG = false;
 
     protected final SparseArray<GradientColors[]> mGradientColors;
     private final ArrayList<WeakReference<OnColorsChangedListener>> mOnColorsChangedListeners;
@@ -147,6 +149,9 @@
 
     @Override
     public void onColorsChanged(WallpaperColors colors, int which) {
+        if (DEBUG) {
+            Log.d(TAG, "New wallpaper colors for " + which + ": " + colors);
+        }
         boolean changed = false;
         if ((which & WallpaperManager.FLAG_LOCK) != 0) {
             mLockColors = colors;
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 8c805ac..d49d572 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -28,7 +28,6 @@
 import android.graphics.Point;
 import android.net.Uri;
 import android.os.Binder;
-import android.os.Build;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.FileObserver;
@@ -39,6 +38,7 @@
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsProvider;
 import android.provider.MediaStore;
+import android.provider.MetadataReader;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
@@ -52,6 +52,7 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
@@ -73,7 +74,6 @@
 
     private Handler mHandler;
 
-    private static final String MIMETYPE_PDF = "application/pdf";
     private static final String MIMETYPE_JPEG = "image/jpeg";
     private static final String MIMETYPE_JPG = "image/jpg";
     private static final String MIMETYPE_OCTET_STREAM = "application/octet-stream";
@@ -120,26 +120,31 @@
 
         if (!file.isFile()) {
             Log.w(TAG, "Can't stream non-regular file. Returning empty metadata.");
-            return Bundle.EMPTY;
+            return null;
         }
 
         if (!file.canRead()) {
             Log.w(TAG, "Can't stream non-readable file. Returning empty metadata.");
-            return Bundle.EMPTY;
+            return null;
         }
 
-        String filePath = file.getAbsolutePath();
-        FileInputStream stream = new FileInputStream(filePath);
+        String mimeType = getTypeForFile(file);
+        if (!MetadataReader.isSupportedMimeType(mimeType)) {
+            return null;
+        }
 
+        InputStream stream = null;
         try {
-            return getDocumentMetadataFromStream(stream, getTypeForFile(file));
+            Bundle metadata = new Bundle();
+            stream = new FileInputStream(file.getAbsolutePath());
+            MetadataReader.getMetadata(metadata, stream, mimeType, null);
+            return metadata;
         } catch (IOException e) {
             Log.e(TAG, "An error occurred retrieving the metadata", e);
+            return null;
         } finally {
             IoUtils.closeQuietly(stream);
         }
-
-        return null;
     }
 
     protected final List<String> findDocumentPath(File parent, File doc)
@@ -430,13 +435,6 @@
             String documentId, Point sizeHint, CancellationSignal signal)
             throws FileNotFoundException {
         final File file = getFileForDocId(documentId);
-        if (getTypeForFile(file).equals(MIMETYPE_PDF)) {
-            try {
-                return PdfUtils.openPdfThumbnail(file, sizeHint);
-            } catch (Exception e) {
-                Log.v(TAG, "Could not load PDF's thumbnail", e);
-            }
-        }
         return DocumentsContract.openImageThumbnail(file);
     }
 
@@ -466,9 +464,7 @@
 
         final String mimeType = getTypeForFile(file);
         final String displayName = file.getName();
-        // As of right now, we aren't sure on the performance affect of loading all PDF Thumbnails
-        // Until a solution is found, it will be behind a debuggable flag.
-        if (mimeType.startsWith("image/") || mimeType.equals(MIMETYPE_PDF)) {
+        if (mimeType.startsWith("image/")) {
             flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
         }
 
@@ -502,7 +498,7 @@
     }
 
     protected boolean typeSupportsMetadata(String mimeType) {
-        return MIMETYPE_JPG.equals(mimeType) || MIMETYPE_JPEG.equals(mimeType);
+        return MetadataReader.isSupportedMimeType(mimeType);
     }
 
     private static String getTypeForName(String name) {
diff --git a/core/java/com/android/internal/content/PdfUtils.java b/core/java/com/android/internal/content/PdfUtils.java
deleted file mode 100644
index 174a9d3..0000000
--- a/core/java/com/android/internal/content/PdfUtils.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.content;
-
-import android.annotation.Nullable;
-import android.content.res.AssetFileDescriptor;
-import android.graphics.Bitmap;
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.graphics.pdf.PdfRenderer;
-import android.os.AsyncTask;
-import android.os.ParcelFileDescriptor;
-
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-
-/**
- * Utils class for extracting PDF Thumbnails
- */
-public final class PdfUtils {
-
-    private PdfUtils() {
-    }
-
-    /**
-     * Returns the front page of the pdf as a thumbnail
-     * @param file Given PDF File
-     * @param size Cropping of the front page.
-     * @return AssetFileDescriptor containing the thumbnail as a bitmap.
-     * @throws IOException if the file isn't a pdf or if the file doesn't exist.
-     */
-    public static @Nullable AssetFileDescriptor openPdfThumbnail(File file, Point size)
-            throws IOException {
-        // Create the bitmap of the PDF's first page
-        ParcelFileDescriptor pdfDescriptor =
-                ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
-        PdfRenderer renderer = new PdfRenderer(pdfDescriptor);
-        PdfRenderer.Page frontPage = renderer.openPage(0);
-        Bitmap thumbnail = Bitmap.createBitmap(frontPage.getWidth(), frontPage.getHeight(),
-                Bitmap.Config.ARGB_8888);
-
-        frontPage.render(thumbnail, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
-
-        thumbnail = crop(thumbnail, size.x, size.y, .5f, 0f);
-
-        // Create an AssetFileDescriptor that contains the Bitmap's information
-        final ByteArrayOutputStream out = new ByteArrayOutputStream();
-        // Quality is an integer that determines how much compression is used.
-        // However, this integer is ignored when using the PNG format
-        int quality = 100;
-        // The use of Bitmap.CompressFormat.JPEG leads to a black PDF background on the thumbnail
-        thumbnail.compress(Bitmap.CompressFormat.PNG, quality, out);
-
-        final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
-
-        final ParcelFileDescriptor[] fds = ParcelFileDescriptor.createReliablePipe();
-        new AsyncTask<Object, Object, Object>() {
-            @Override
-            protected Object doInBackground(Object... params) {
-                final FileOutputStream fos = new FileOutputStream(fds[1].getFileDescriptor());
-                try {
-                    Streams.copy(in, fos);
-                } catch (IOException e) {
-                    throw new RuntimeException(e);
-                }
-                IoUtils.closeQuietly(fds[1]);
-                try {
-                    pdfDescriptor.close();
-                } catch (IOException e) {
-                    throw new RuntimeException(e);
-                }
-                return null;
-            }
-        }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
-        pdfDescriptor.close();
-        return new AssetFileDescriptor(fds[0], 0, AssetFileDescriptor.UNKNOWN_LENGTH);
-    }
-
-    /**
-     * Returns a new Bitmap copy with a crop effect depending on the crop anchor given. 0.5f is like
-     * {@link android.widget.ImageView.ScaleType#CENTER_CROP}. The crop anchor will be be nudged
-     * so the entire cropped bitmap will fit inside the src. May return the input bitmap if no
-     * scaling is necessary.
-     *
-     *
-     * Example of changing verticalCenterPercent:
-     *   _________            _________
-     *  |         |          |         |
-     *  |         |          |_________|
-     *  |         |          |         |/___0.3f
-     *  |---------|          |_________|\
-     *  |         |<---0.5f  |         |
-     *  |---------|          |         |
-     *  |         |          |         |
-     *  |         |          |         |
-     *  |_________|          |_________|
-     *
-     * @param src original bitmap of any size
-     * @param w desired width in px
-     * @param h desired height in px
-     * @param horizontalCenterPercent determines which part of the src to crop from. Range from 0
-     *                                .0f to 1.0f. The value determines which part of the src
-     *                                maps to the horizontal center of the resulting bitmap.
-     * @param verticalCenterPercent determines which part of the src to crop from. Range from 0
-     *                              .0f to 1.0f. The value determines which part of the src maps
-     *                              to the vertical center of the resulting bitmap.
-     * @return a copy of src conforming to the given width and height, or src itself if it already
-     *         matches the given width and height
-     *
-     */
-    private static Bitmap crop(final Bitmap src, final int w, final int h,
-            final float horizontalCenterPercent, final float verticalCenterPercent) {
-        if (horizontalCenterPercent < 0 || horizontalCenterPercent > 1 || verticalCenterPercent < 0
-                || verticalCenterPercent > 1) {
-            throw new IllegalArgumentException(
-                    "horizontalCenterPercent and verticalCenterPercent must be between 0.0f and "
-                            + "1.0f, inclusive.");
-        }
-        final int srcWidth = src.getWidth();
-        final int srcHeight = src.getHeight();
-        // exit early if no resize/crop needed
-        if (w == srcWidth && h == srcHeight) {
-            return src;
-        }
-        final Matrix m = new Matrix();
-        final float scale = Math.max(
-                (float) w / srcWidth,
-                (float) h / srcHeight);
-        m.setScale(scale, scale);
-        final int srcCroppedW, srcCroppedH;
-        int srcX, srcY;
-        srcCroppedW = Math.round(w / scale);
-        srcCroppedH = Math.round(h / scale);
-        srcX = (int) (srcWidth * horizontalCenterPercent - srcCroppedW / 2);
-        srcY = (int) (srcHeight * verticalCenterPercent - srcCroppedH / 2);
-        // Nudge srcX and srcY to be within the bounds of src
-        srcX = Math.max(Math.min(srcX, srcWidth - srcCroppedW), 0);
-        srcY = Math.max(Math.min(srcY, srcHeight - srcCroppedH), 0);
-        final Bitmap cropped = Bitmap.createBitmap(src, srcX, srcY,
-                srcCroppedW, srcCroppedH, m, true);
-        return cropped;
-    }
-
-}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index d7ecaec..219d7b5 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -119,7 +119,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 163 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 164 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS;
@@ -157,6 +157,15 @@
     // Number of transmit power states the Bluetooth controller can be in.
     private static final int NUM_BT_TX_LEVELS = 1;
 
+    /**
+     * Holding a wakelock costs more than just using the cpu.
+     * Currently, we assign only half the cpu time to an app that is running but
+     * not holding a wakelock. The apps holding wakelocks get the rest of the blame.
+     * If no app is holding a wakelock, then the distribution is normal.
+     */
+    @VisibleForTesting
+    public static final int WAKE_LOCK_WEIGHT = 50;
+
     protected Clocks mClocks;
 
     private final JournaledFile mFile;
@@ -171,9 +180,12 @@
     private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
     private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
 
-    private final KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader();
-    private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
-    private final KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader =
+    @VisibleForTesting
+    protected KernelUidCpuTimeReader mKernelUidCpuTimeReader = new KernelUidCpuTimeReader();
+    @VisibleForTesting
+    protected KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
+    @VisibleForTesting
+    protected KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader =
             new KernelUidCpuFreqTimeReader();
 
     private final KernelMemoryBandwidthStats mKernelMemoryBandwidthStats
@@ -197,10 +209,12 @@
     public static abstract class UserInfoProvider {
         private int[] userIds;
         protected abstract @Nullable int[] getUserIds();
-        private final void refreshUserIds() {
+        @VisibleForTesting
+        public final void refreshUserIds() {
             userIds = getUserIds();
         }
-        private final boolean exists(int userId) {
+        @VisibleForTesting
+        public boolean exists(int userId) {
             return userIds != null ? ArrayUtils.contains(userIds, userId) : true;
         }
     }
@@ -273,7 +287,8 @@
 
     public final MyHandler mHandler;
     private ExternalStatsSync mExternalSync = null;
-    private UserInfoProvider mUserInfoProvider = null;
+    @VisibleForTesting
+    protected UserInfoProvider mUserInfoProvider = null;
 
     private BatteryCallback mCallback;
 
@@ -291,7 +306,8 @@
     // elapsed time by the number of active timers to arrive at that timer's share of the time.
     // In order to do this, we must refresh each timer whenever the number of active timers
     // changes.
-    final ArrayList<StopwatchTimer> mPartialTimers = new ArrayList<>();
+    @VisibleForTesting
+    protected ArrayList<StopwatchTimer> mPartialTimers = new ArrayList<>();
     final ArrayList<StopwatchTimer> mFullTimers = new ArrayList<>();
     final ArrayList<StopwatchTimer> mWindowTimers = new ArrayList<>();
     final ArrayList<StopwatchTimer> mDrawTimers = new ArrayList<>();
@@ -308,7 +324,8 @@
     final ArrayList<StopwatchTimer> mBluetoothScanOnTimers = new ArrayList<>();
 
     // Last partial timers we use for distributing CPU usage.
-    final ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<>();
+    @VisibleForTesting
+    protected ArrayList<StopwatchTimer> mLastPartialTimers = new ArrayList<>();
 
     // These are the objects that will want to do something when the device
     // is unplugged from power.
@@ -536,7 +553,8 @@
      * in to power.
      */
     boolean mOnBattery;
-    boolean mOnBatteryInternal;
+    @VisibleForTesting
+    protected boolean mOnBatteryInternal;
 
     /**
      * External reporting of whether the device is actually charging.
@@ -606,7 +624,8 @@
 
     private long[] mCpuFreqs;
 
-    private PowerProfile mPowerProfile;
+    @VisibleForTesting
+    protected PowerProfile mPowerProfile;
 
     /*
      * Holds a SamplingTimer associated with each kernel wakelock name being tracked.
@@ -2020,7 +2039,8 @@
          * For partial wake locks, keep track of whether we are in the list
          * to consume CPU cycles.
          */
-        boolean mInList;
+        @VisibleForTesting
+        public boolean mInList;
 
         public StopwatchTimer(Clocks clocks, Uid uid, int type, ArrayList<StopwatchTimer> timerPool,
                 TimeBase timeBase, Parcel in) {
@@ -3632,7 +3652,12 @@
     }
 
     public void noteUidProcessStateLocked(int uid, int state) {
-        uid = mapUid(uid);
+        int parentUid = mapUid(uid);
+        if (uid != parentUid) {
+            // Isolated UIDs process state is already rolled up into parent, so no need to track
+            // Otherwise the parent's process state will get downgraded incorrectly
+            return;
+        }
         getUidStatsLocked(uid).updateUidProcessStateLocked(state);
     }
 
@@ -8039,6 +8064,7 @@
 
             /**
              * Number of times wakeup alarms have occurred for this app.
+             * On screen-off timebase starting in report v25.
              */
             ArrayMap<String, Counter> mWakeupAlarms = new ArrayMap<>();
 
@@ -8067,7 +8093,7 @@
                 mWakeupAlarms.clear();
                 for (int i=0; i<numWA; i++) {
                     String tag = in.readString();
-                    mWakeupAlarms.put(tag, new Counter(mBsi.mOnBatteryTimeBase, in));
+                    mWakeupAlarms.put(tag, new Counter(mBsi.mOnBatteryScreenOffTimeBase, in));
                 }
 
                 int numServs = in.readInt();
@@ -8106,7 +8132,7 @@
             public void noteWakeupAlarmLocked(String tag) {
                 Counter c = mWakeupAlarms.get(tag);
                 if (c == null) {
-                    c = new Counter(mBsi.mOnBatteryTimeBase);
+                    c = new Counter(mBsi.mOnBatteryScreenOffTimeBase);
                     mWakeupAlarms.put(tag, c);
                 }
                 c.stepAtomic();
@@ -10262,169 +10288,83 @@
             Slog.d(TAG, "!Cpu updating!");
         }
 
-        // Holding a wakelock costs more than just using the cpu.
-        // Currently, we assign only half the cpu time to an app that is running but
-        // not holding a wakelock. The apps holding wakelocks get the rest of the blame.
-        // If no app is holding a wakelock, then the distribution is normal.
-        final int wakelockWeight = 50;
-
-        int numWakelocks = 0;
-
-        // Calculate how many wakelocks we have to distribute amongst. The system is excluded.
-        // Only distribute cpu power to wakelocks if the screen is off and we're on battery.
-        final int numPartialTimers = mPartialTimers.size();
+        // Calculate the wakelocks we have to distribute amongst. The system is excluded as it is
+        // usually holding the wakelock on behalf of an app.
+        // And Only distribute cpu power to wakelocks if the screen is off and we're on battery.
+        ArrayList<StopwatchTimer> partialTimersToConsider = null;
         if (mOnBatteryScreenOffTimeBase.isRunning()) {
-            for (int i = 0; i < numPartialTimers; i++) {
+            partialTimersToConsider = new ArrayList<>();
+            for (int i = mPartialTimers.size() - 1; i >= 0; --i) {
                 final StopwatchTimer timer = mPartialTimers.get(i);
+                // Since the collection and blaming of wakelocks can be scheduled to run after
+                // some delay, the mPartialTimers list may have new entries. We can't blame
+                // the newly added timer for past cpu time, so we only consider timers that
+                // were present for one round of collection. Once a timer has gone through
+                // a round of collection, its mInList field is set to true.
                 if (timer.mInList && timer.mUid != null && timer.mUid.mUid != Process.SYSTEM_UID) {
-                    // Since the collection and blaming of wakelocks can be scheduled to run after
-                    // some delay, the mPartialTimers list may have new entries. We can't blame
-                    // the newly added timer for past cpu time, so we only consider timers that
-                    // were present for one round of collection. Once a timer has gone through
-                    // a round of collection, its mInList field is set to true.
-                    numWakelocks++;
+                    partialTimersToConsider.add(timer);
                 }
             }
         }
+        markPartialTimersAsEligible();
 
-        final int numWakelocksF = numWakelocks;
-        mTempTotalCpuUserTimeUs = 0;
-        mTempTotalCpuSystemTimeUs = 0;
+        // When the battery is not on, we don't attribute the cpu times to any timers but we still
+        // need to take the snapshots.
+        if (!mOnBatteryInternal) {
+            mKernelUidCpuTimeReader.readDelta(null);
+            mKernelUidCpuFreqTimeReader.readDelta(null);
+            for (int cluster = mKernelCpuSpeedReaders.length - 1; cluster >= 0; --cluster) {
+                mKernelCpuSpeedReaders[cluster].readDelta();
+            }
+            return;
+        }
 
-        final SparseLongArray updatedUids = new SparseLongArray();
-
-        // Read the CPU data for each UID. This will internally generate a snapshot so next time
-        // we read, we get a delta. If we are to distribute the cpu time, then do so. Otherwise
-        // we just ignore the data.
-        final long startTimeMs = mClocks.uptimeMillis();
         mUserInfoProvider.refreshUserIds();
-        mKernelUidCpuTimeReader.readDelta(!mOnBatteryInternal ? null :
-                new KernelUidCpuTimeReader.Callback() {
-                    @Override
-                    public void onUidCpuTime(int uid, long userTimeUs, long systemTimeUs) {
-                        uid = mapUid(uid);
-                        if (Process.isIsolated(uid)) {
-                            // This could happen if the isolated uid mapping was removed before
-                            // that process was actually killed.
-                            mKernelUidCpuTimeReader.removeUid(uid);
-                            Slog.d(TAG, "Got readings for an isolated uid with"
-                                    + " no mapping to owning uid: " + uid);
-                            return;
-                        }
-                        if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
-                            Slog.d(TAG, "Got readings for an invalid user's uid " + uid);
-                            mKernelUidCpuTimeReader.removeUid(uid);
-                            return;
-                        }
-                        final Uid u = getUidStatsLocked(uid);
-
-                        // Accumulate the total system and user time.
-                        mTempTotalCpuUserTimeUs += userTimeUs;
-                        mTempTotalCpuSystemTimeUs += systemTimeUs;
-
-                        StringBuilder sb = null;
-                        if (DEBUG_ENERGY_CPU) {
-                            sb = new StringBuilder();
-                            sb.append("  got time for uid=").append(u.mUid).append(": u=");
-                            TimeUtils.formatDuration(userTimeUs / 1000, sb);
-                            sb.append(" s=");
-                            TimeUtils.formatDuration(systemTimeUs / 1000, sb);
-                            sb.append("\n");
-                        }
-
-                        if (numWakelocksF > 0) {
-                            // We have wakelocks being held, so only give a portion of the
-                            // time to the process. The rest will be distributed among wakelock
-                            // holders.
-                            userTimeUs = (userTimeUs * wakelockWeight) / 100;
-                            systemTimeUs = (systemTimeUs * wakelockWeight) / 100;
-                        }
-
-                        if (sb != null) {
-                            sb.append("  adding to uid=").append(u.mUid).append(": u=");
-                            TimeUtils.formatDuration(userTimeUs / 1000, sb);
-                            sb.append(" s=");
-                            TimeUtils.formatDuration(systemTimeUs / 1000, sb);
-                            Slog.d(TAG, sb.toString());
-                        }
-
-                        u.mUserCpuTime.addCountLocked(userTimeUs);
-                        u.mSystemCpuTime.addCountLocked(systemTimeUs);
-                        updatedUids.put(u.getUid(), userTimeUs + systemTimeUs);
-                    }
-                });
-
+        final SparseLongArray updatedUids = new SparseLongArray();
+        readKernelUidCpuTimesLocked(partialTimersToConsider, updatedUids);
+        updateClusterSpeedTimes(updatedUids);
         if (updateCpuFreqData) {
             readKernelUidCpuFreqTimesLocked();
         }
+    }
 
-        final long elapse = (mClocks.uptimeMillis() - startTimeMs);
-        if (DEBUG_ENERGY_CPU || (elapse >= 100)) {
-            Slog.d(TAG, "Reading cpu stats took " + elapse + " ms");
-        }
+    /**
+     * Mark the current partial timers as gone through a collection so that they will be
+     * considered in the next cpu times distribution to wakelock holders.
+     */
+    @VisibleForTesting
+    public void markPartialTimersAsEligible() {
+        if (ArrayUtils.referenceEquals(mPartialTimers, mLastPartialTimers)) {
+            // No difference, so each timer is now considered for the next collection.
+            for (int i = mPartialTimers.size() - 1; i >= 0; --i) {
+                mPartialTimers.get(i).mInList = true;
+            }
+        } else {
+            // The lists are different, meaning we added (or removed a timer) since the last
+            // collection.
+            for (int i = mLastPartialTimers.size() - 1; i >= 0; --i) {
+                mLastPartialTimers.get(i).mInList = false;
+            }
+            mLastPartialTimers.clear();
 
-        if (mOnBatteryInternal && numWakelocks > 0) {
-            // Distribute a portion of the total cpu time to wakelock holders.
-            mTempTotalCpuUserTimeUs = (mTempTotalCpuUserTimeUs * (100 - wakelockWeight)) / 100;
-            mTempTotalCpuSystemTimeUs =
-                    (mTempTotalCpuSystemTimeUs * (100 - wakelockWeight)) / 100;
-
-            for (int i = 0; i < numPartialTimers; i++) {
+            // Mark the current timers as gone through a collection.
+            final int numPartialTimers = mPartialTimers.size();
+            for (int i = 0; i < numPartialTimers; ++i) {
                 final StopwatchTimer timer = mPartialTimers.get(i);
-
-                // The system does not share any blame, as it is usually holding the wakelock
-                // on behalf of an app.
-                if (timer.mInList && timer.mUid != null && timer.mUid.mUid != Process.SYSTEM_UID) {
-                    int userTimeUs = (int) (mTempTotalCpuUserTimeUs / numWakelocks);
-                    int systemTimeUs = (int) (mTempTotalCpuSystemTimeUs / numWakelocks);
-
-                    if (DEBUG_ENERGY_CPU) {
-                        StringBuilder sb = new StringBuilder();
-                        sb.append("  Distributing wakelock uid=").append(timer.mUid.mUid)
-                                .append(": u=");
-                        TimeUtils.formatDuration(userTimeUs / 1000, sb);
-                        sb.append(" s=");
-                        TimeUtils.formatDuration(systemTimeUs / 1000, sb);
-                        Slog.d(TAG, sb.toString());
-                    }
-
-                    timer.mUid.mUserCpuTime.addCountLocked(userTimeUs);
-                    timer.mUid.mSystemCpuTime.addCountLocked(systemTimeUs);
-                    final int uid = timer.mUid.getUid();
-                    updatedUids.put(uid, updatedUids.get(uid, 0) + userTimeUs + systemTimeUs);
-
-                    final Uid.Proc proc = timer.mUid.getProcessStatsLocked("*wakelock*");
-                    proc.addCpuTimeLocked(userTimeUs / 1000, systemTimeUs / 1000);
-
-                    mTempTotalCpuUserTimeUs -= userTimeUs;
-                    mTempTotalCpuSystemTimeUs -= systemTimeUs;
-                    numWakelocks--;
-                }
-            }
-
-            if (mTempTotalCpuUserTimeUs > 0 || mTempTotalCpuSystemTimeUs > 0) {
-                // Anything left over is given to the system.
-                if (DEBUG_ENERGY_CPU) {
-                    StringBuilder sb = new StringBuilder();
-                    sb.append("  Distributing lost time to system: u=");
-                    TimeUtils.formatDuration(mTempTotalCpuUserTimeUs / 1000, sb);
-                    sb.append(" s=");
-                    TimeUtils.formatDuration(mTempTotalCpuSystemTimeUs / 1000, sb);
-                    Slog.d(TAG, sb.toString());
-                }
-
-                final Uid u = getUidStatsLocked(Process.SYSTEM_UID);
-                u.mUserCpuTime.addCountLocked(mTempTotalCpuUserTimeUs);
-                u.mSystemCpuTime.addCountLocked(mTempTotalCpuSystemTimeUs);
-                updatedUids.put(Process.SYSTEM_UID, updatedUids.get(Process.SYSTEM_UID, 0)
-                        + mTempTotalCpuUserTimeUs + mTempTotalCpuSystemTimeUs);
-
-                final Uid.Proc proc = u.getProcessStatsLocked("*lost*");
-                proc.addCpuTimeLocked((int) mTempTotalCpuUserTimeUs / 1000,
-                        (int) mTempTotalCpuSystemTimeUs / 1000);
+                timer.mInList = true;
+                mLastPartialTimers.add(timer);
             }
         }
+    }
 
+    /**
+     * Take snapshot of cpu times (aggregated over all uids) at different frequencies and
+     * calculate cpu times spent by each uid at different frequencies.
+     *
+     * @param updatedUids The uids for which times spent at different frequencies are calculated.
+     */
+    @VisibleForTesting
+    public void updateClusterSpeedTimes(@NonNull SparseLongArray updatedUids) {
         long totalCpuClustersTimeMs = 0;
         // Read the time spent for each cluster at various cpu frequencies.
         final long[][] clusterSpeedTimesMs = new long[mKernelCpuSpeedReaders.length][];
@@ -10446,8 +10386,8 @@
                 final long appCpuTimeUs = updatedUids.valueAt(i);
                 // Add the cpu speeds to this UID.
                 final int numClusters = mPowerProfile.getNumCpuClusters();
-                if (u.mCpuClusterSpeedTimesUs == null || u.mCpuClusterSpeedTimesUs.length !=
-                        numClusters) {
+                if (u.mCpuClusterSpeedTimesUs == null ||
+                        u.mCpuClusterSpeedTimesUs.length != numClusters) {
                     u.mCpuClusterSpeedTimesUs = new LongSamplingCounter[numClusters][];
                 }
 
@@ -10471,66 +10411,155 @@
                 }
             }
         }
+    }
 
-        // See if there is a difference in wakelocks between this collection and the last
-        // collection.
-        if (ArrayUtils.referenceEquals(mPartialTimers, mLastPartialTimers)) {
-            // No difference, so each timer is now considered for the next collection.
-            for (int i = 0; i < numPartialTimers; i++) {
-                mPartialTimers.get(i).mInList = true;
-            }
-        } else {
-            // The lists are different, meaning we added (or removed a timer) since the last
-            // collection.
-            final int numLastPartialTimers = mLastPartialTimers.size();
-            for (int i = 0; i < numLastPartialTimers; i++) {
-                mLastPartialTimers.get(i).mInList = false;
-            }
-            mLastPartialTimers.clear();
+    /**
+     * Take a snapshot of the cpu times spent by each uid and update the corresponding counters.
+     * If {@param partialTimers} is not null and empty, then we assign a portion of cpu times to
+     * wakelock holders.
+     *
+     * @param partialTimers The wakelock holders among which the cpu times will be distributed.
+     * @param updatedUids If not null, then the uids found in the snapshot will be added to this.
+     */
+    @VisibleForTesting
+    public void readKernelUidCpuTimesLocked(@Nullable ArrayList<StopwatchTimer> partialTimers,
+            @Nullable SparseLongArray updatedUids) {
+        mTempTotalCpuUserTimeUs = mTempTotalCpuSystemTimeUs = 0;
+        final int numWakelocks = partialTimers == null ? 0 : partialTimers.size();
+        final long startTimeMs = mClocks.uptimeMillis();
 
-            // Mark the current timers as gone through a collection.
-            for (int i = 0; i < numPartialTimers; i++) {
-                final StopwatchTimer timer = mPartialTimers.get(i);
-                timer.mInList = true;
-                mLastPartialTimers.add(timer);
+        mKernelUidCpuTimeReader.readDelta((uid, userTimeUs, systemTimeUs) -> {
+            uid = mapUid(uid);
+            if (Process.isIsolated(uid)) {
+                // This could happen if the isolated uid mapping was removed before that process
+                // was actually killed.
+                mKernelUidCpuTimeReader.removeUid(uid);
+                Slog.d(TAG, "Got readings for an isolated uid with no mapping: " + uid);
+                return;
+            }
+            if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
+                Slog.d(TAG, "Got readings for an invalid user's uid " + uid);
+                mKernelUidCpuTimeReader.removeUid(uid);
+                return;
+            }
+            final Uid u = getUidStatsLocked(uid);
+
+            // Accumulate the total system and user time.
+            mTempTotalCpuUserTimeUs += userTimeUs;
+            mTempTotalCpuSystemTimeUs += systemTimeUs;
+
+            StringBuilder sb = null;
+            if (DEBUG_ENERGY_CPU) {
+                sb = new StringBuilder();
+                sb.append("  got time for uid=").append(u.mUid).append(": u=");
+                TimeUtils.formatDuration(userTimeUs / 1000, sb);
+                sb.append(" s=");
+                TimeUtils.formatDuration(systemTimeUs / 1000, sb);
+                sb.append("\n");
+            }
+
+            if (numWakelocks > 0) {
+                // We have wakelocks being held, so only give a portion of the
+                // time to the process. The rest will be distributed among wakelock
+                // holders.
+                userTimeUs = (userTimeUs * WAKE_LOCK_WEIGHT) / 100;
+                systemTimeUs = (systemTimeUs * WAKE_LOCK_WEIGHT) / 100;
+            }
+
+            if (sb != null) {
+                sb.append("  adding to uid=").append(u.mUid).append(": u=");
+                TimeUtils.formatDuration(userTimeUs / 1000, sb);
+                sb.append(" s=");
+                TimeUtils.formatDuration(systemTimeUs / 1000, sb);
+                Slog.d(TAG, sb.toString());
+            }
+
+            u.mUserCpuTime.addCountLocked(userTimeUs);
+            u.mSystemCpuTime.addCountLocked(systemTimeUs);
+            if (updatedUids != null) {
+                updatedUids.put(u.getUid(), userTimeUs + systemTimeUs);
+            }
+        });
+
+        final long elapsedTimeMs = mClocks.uptimeMillis() - startTimeMs;
+        if (DEBUG_ENERGY_CPU || elapsedTimeMs >= 100) {
+            Slog.d(TAG, "Reading cpu stats took " + elapsedTimeMs + "ms");
+        }
+
+        if (numWakelocks > 0) {
+            // Distribute a portion of the total cpu time to wakelock holders.
+            mTempTotalCpuUserTimeUs = (mTempTotalCpuUserTimeUs * (100 - WAKE_LOCK_WEIGHT)) / 100;
+            mTempTotalCpuSystemTimeUs =
+                    (mTempTotalCpuSystemTimeUs * (100 - WAKE_LOCK_WEIGHT)) / 100;
+
+            for (int i = 0; i < numWakelocks; ++i) {
+                final StopwatchTimer timer = partialTimers.get(i);
+                final int userTimeUs = (int) (mTempTotalCpuUserTimeUs / (numWakelocks - i));
+                final int systemTimeUs = (int) (mTempTotalCpuSystemTimeUs / (numWakelocks - i));
+
+                if (DEBUG_ENERGY_CPU) {
+                    final StringBuilder sb = new StringBuilder();
+                    sb.append("  Distributing wakelock uid=").append(timer.mUid.mUid)
+                            .append(": u=");
+                    TimeUtils.formatDuration(userTimeUs / 1000, sb);
+                    sb.append(" s=");
+                    TimeUtils.formatDuration(systemTimeUs / 1000, sb);
+                    Slog.d(TAG, sb.toString());
+                }
+
+                timer.mUid.mUserCpuTime.addCountLocked(userTimeUs);
+                timer.mUid.mSystemCpuTime.addCountLocked(systemTimeUs);
+                if (updatedUids != null) {
+                    final int uid = timer.mUid.getUid();
+                    updatedUids.put(uid, updatedUids.get(uid, 0) + userTimeUs + systemTimeUs);
+                }
+
+                final Uid.Proc proc = timer.mUid.getProcessStatsLocked("*wakelock*");
+                proc.addCpuTimeLocked(userTimeUs / 1000, systemTimeUs / 1000);
+
+                mTempTotalCpuUserTimeUs -= userTimeUs;
+                mTempTotalCpuSystemTimeUs -= systemTimeUs;
             }
         }
     }
 
-    void readKernelUidCpuFreqTimesLocked() {
-        mKernelUidCpuFreqTimeReader.readDelta(!mOnBatteryInternal ? null :
-                new KernelUidCpuFreqTimeReader.Callback() {
-                    @Override
-                    public void onCpuFreqs(long[] cpuFreqs) {
-                        mCpuFreqs = cpuFreqs;
-                    }
+    /**
+     * Take a snapshot of the cpu times spent by each uid in each freq and update the
+     * corresponding counters.
+     */
+    @VisibleForTesting
+    public void readKernelUidCpuFreqTimesLocked() {
+        mKernelUidCpuFreqTimeReader.readDelta(new KernelUidCpuFreqTimeReader.Callback() {
+            @Override
+            public void onCpuFreqs(long[] cpuFreqs) {
+                mCpuFreqs = cpuFreqs;
+            }
 
-                    @Override
-                    public void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs) {
-                        uid = mapUid(uid);
-                        if (Process.isIsolated(uid)) {
-                            mKernelUidCpuFreqTimeReader.removeUid(uid);
-                            Slog.d(TAG, "Got freq readings for an isolated uid with"
-                                    + " no mapping to owning uid: " + uid);
-                            return;
-                        }
-                        if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
-                            Slog.d(TAG, "Got readings for an invalid user's uid " + uid);
-                            mKernelUidCpuFreqTimeReader.removeUid(uid);
-                            return;
-                        }
-                        final Uid u = getUidStatsLocked(uid);
-                        if (u.mCpuFreqTimeMs == null) {
-                            u.mCpuFreqTimeMs = new LongSamplingCounterArray(mOnBatteryTimeBase);
-                        }
-                        u.mCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs);
-                        if (u.mScreenOffCpuFreqTimeMs == null) {
-                            u.mScreenOffCpuFreqTimeMs = new LongSamplingCounterArray(
-                                    mOnBatteryScreenOffTimeBase);
-                        }
-                        u.mScreenOffCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs);
-                    }
-                });
+            @Override
+            public void onUidCpuFreqTime(int uid, long[] cpuFreqTimeMs) {
+                uid = mapUid(uid);
+                if (Process.isIsolated(uid)) {
+                    mKernelUidCpuFreqTimeReader.removeUid(uid);
+                    Slog.d(TAG, "Got freq readings for an isolated uid with no mapping: " + uid);
+                    return;
+                }
+                if (!mUserInfoProvider.exists(UserHandle.getUserId(uid))) {
+                    Slog.d(TAG, "Got freq readings for an invalid user's uid " + uid);
+                    mKernelUidCpuFreqTimeReader.removeUid(uid);
+                    return;
+                }
+                final Uid u = getUidStatsLocked(uid);
+                if (u.mCpuFreqTimeMs == null) {
+                    u.mCpuFreqTimeMs = new LongSamplingCounterArray(mOnBatteryTimeBase);
+                }
+                u.mCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs);
+                if (u.mScreenOffCpuFreqTimeMs == null) {
+                    u.mScreenOffCpuFreqTimeMs = new LongSamplingCounterArray(
+                            mOnBatteryScreenOffTimeBase);
+                }
+                u.mScreenOffCpuFreqTimeMs.addCountLocked(cpuFreqTimeMs);
+            }
+        });
     }
 
     boolean setChargingLocked(boolean charging) {
@@ -11865,7 +11894,7 @@
                 p.mWakeupAlarms.clear();
                 for (int iwa=0; iwa<NWA; iwa++) {
                     String tag = in.readString();
-                    Counter c = new Counter(mOnBatteryTimeBase);
+                    Counter c = new Counter(mOnBatteryScreenOffTimeBase);
                     c.readSummaryFromParcelLocked(in);
                     p.mWakeupAlarms.put(tag, c);
                 }
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index dbb6e93..f0b47de 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -34,7 +34,7 @@
 import java.util.stream.Stream;
 
 /**
- * Utility methods for dealing with (typically {@link Nullable}) {@link Collection}s
+ * Utility methods for dealing with (typically {@code Nullable}) {@link Collection}s
  *
  * Unless a method specifies otherwise, a null value for a collection is treated as an empty
  * collection of that type.
diff --git a/core/java/com/android/internal/util/JournaledFile.java b/core/java/com/android/internal/util/JournaledFile.java
index 9f775d3..5372fc0 100644
--- a/core/java/com/android/internal/util/JournaledFile.java
+++ b/core/java/com/android/internal/util/JournaledFile.java
@@ -20,7 +20,7 @@
 import java.io.IOException;
 
 /**
- * @deprecated Use {@link com.android.internal.os.AtomicFile} instead.  It would
+ * @deprecated Use {@code AtomicFile} instead.  It would
  * be nice to update all existing uses of this to switch to AtomicFile, but since
  * their on-file semantics are slightly different that would run the risk of losing
  * data if at the point of the platform upgrade to the new code it would need to
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 9462a06..e6c49f1 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -67,8 +67,7 @@
 
     private static final String TAG = "LockPatternUtils";
     private static final boolean DEBUG = false;
-    private static final boolean FRP_CREDENTIAL_ENABLED =
-            Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.frpcredential.enable", false);
+    private static final boolean FRP_CREDENTIAL_ENABLED = true;
 
     /**
      * The key to identify when the lock pattern enabled flag is being accessed for legacy reasons.
diff --git a/core/java/com/android/internal/widget/RecyclerView.java b/core/java/com/android/internal/widget/RecyclerView.java
index 0cf3164..7abc76a 100644
--- a/core/java/com/android/internal/widget/RecyclerView.java
+++ b/core/java/com/android/internal/widget/RecyclerView.java
@@ -132,8 +132,6 @@
  * <p>
  * When writing a {@link LayoutManager} you almost always want to use layout positions whereas when
  * writing an {@link Adapter}, you probably want to use adapter positions.
- *
- * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_layoutManager
  */
 public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild {
 
@@ -9591,11 +9589,6 @@
         /**
          * Parse the xml attributes to get the most common properties used by layout managers.
          *
-         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation
-         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount
-         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout
-         * @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd
-         *
          * @return an object containing the properties as specified in the attrs.
          */
         public static Properties getProperties(Context context, AttributeSet attrs,
diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java
index 5375651..a96b5dd 100644
--- a/core/java/com/android/server/backup/SystemBackupAgent.java
+++ b/core/java/com/android/server/backup/SystemBackupAgent.java
@@ -35,7 +35,8 @@
 import java.io.IOException;
 
 /**
- * Backup agent for various system-managed data, currently just the system wallpaper
+ * Backup agent for various system-managed data.  Wallpapers are now handled by a
+ * separate package, but we still process restores from legacy datasets here.
  */
 public class SystemBackupAgent extends BackupAgentHelper {
     private static final String TAG = "SystemBackupAgent";
@@ -61,16 +62,19 @@
     // TODO: http://b/22388012
     private static final String WALLPAPER_IMAGE_DIR =
             Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM).getAbsolutePath();
-    private static final String WALLPAPER_IMAGE = WallpaperBackupHelper.WALLPAPER_IMAGE;
+    public static final String WALLPAPER_IMAGE =
+            new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM),
+                    "wallpaper").getAbsolutePath();
 
     // TODO: Will need to change if backing up non-primary user's wallpaper
     // TODO: http://b/22388012
     private static final String WALLPAPER_INFO_DIR =
             Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM).getAbsolutePath();
-    private static final String WALLPAPER_INFO = WallpaperBackupHelper.WALLPAPER_INFO;
+    public static final String WALLPAPER_INFO =
+            new File(Environment.getUserSystemDirectory(UserHandle.USER_SYSTEM),
+                    "wallpaper_info.xml").getAbsolutePath();
     // Use old keys to keep legacy data compatibility and avoid writing two wallpapers
     private static final String WALLPAPER_IMAGE_KEY = WallpaperBackupHelper.WALLPAPER_IMAGE_KEY;
-    private static final String WALLPAPER_INFO_KEY = WallpaperBackupHelper.WALLPAPER_INFO_KEY;
 
     private WallpaperBackupHelper mWallpaperHelper = null;
 
@@ -98,13 +102,11 @@
         // Slot in a restore helper for the older wallpaper backup schema to support restore
         // from devices still generating data in that format.
         mWallpaperHelper = new WallpaperBackupHelper(this,
-                new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO },
-                new String[] { WALLPAPER_IMAGE_KEY, WALLPAPER_INFO_KEY} );
+                new String[] { WALLPAPER_IMAGE_KEY} );
         addHelper(WALLPAPER_HELPER, mWallpaperHelper);
 
         // On restore, we also support a long-ago wallpaper data schema "system_files"
         addHelper("system_files", new WallpaperBackupHelper(this,
-                new String[] { WALLPAPER_IMAGE },
                 new String[] { WALLPAPER_IMAGE_KEY} ));
 
         addHelper(SYNC_SETTINGS_HELPER, new AccountSyncSettingsBackupHelper(this));
@@ -115,27 +117,12 @@
         addHelper(SHORTCUT_MANAGER_HELPER, new ShortcutBackupHelper());
         addHelper(ACCOUNT_MANAGER_HELPER, new AccountManagerBackupHelper());
 
-        try {
-            super.onRestore(data, appVersionCode, newState);
-
-            IWallpaperManager wallpaper = (IWallpaperManager) ServiceManager.getService(
-                    Context.WALLPAPER_SERVICE);
-            if (wallpaper != null) {
-                try {
-                    wallpaper.settingsRestored();
-                } catch (RemoteException re) {
-                    Slog.e(TAG, "Couldn't restore settings\n" + re);
-                }
-            }
-        } catch (IOException ex) {
-            // If there was a failure, delete everything for the wallpaper, this is too aggressive,
-            // but this is hopefully a rare failure.
-            Slog.d(TAG, "restore failed", ex);
-            (new File(WALLPAPER_IMAGE)).delete();
-            (new File(WALLPAPER_INFO)).delete();
-        }
+        super.onRestore(data, appVersionCode, newState);
     }
 
+    /**
+     * Support for 'adb restore' of legacy archives
+     */
     @Override
     public void onRestoreFile(ParcelFileDescriptor data, long size,
             int type, String domain, String path, long mode, long mtime)
@@ -183,12 +170,4 @@
             }
         }
     }
-
-    @Override
-    public void onRestoreFinished() {
-        // helper will be null following 'adb restore' or other full-data operation
-        if (mWallpaperHelper != null) {
-            mWallpaperHelper.onRestoreFinished();
-        }
-    }
 }
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 1ad8541..5afd067 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -814,6 +814,12 @@
             addOption("-Ximage-compiler-option");
             addOption("--compiled-classes=/system/etc/compiled-classes");
         }
+
+        // If there is a dirty-image-objects file, push it.
+        if (hasFile("/system/etc/dirty-image-objects")) {
+            addOption("-Ximage-compiler-option");
+            addOption("--dirty-image-objects=/system/etc/dirty-image-objects");
+        }
     }
 
     property_get("dalvik.vm.image-dex2oat-flags", dex2oatImageFlagsBuf, "");
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index bfb2511..7b381b4 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -44,7 +44,7 @@
     mWidth = width;
     mHeight = height;
     SkCanvas* canvas = mRecorder->beginRecording(SkIntToScalar(width), SkIntToScalar(height));
-    return Canvas::create_canvas(canvas, Canvas::XformToSRGB::kDefer);
+    return Canvas::create_canvas(canvas);
 }
 
 void Picture::endRecording() {
diff --git a/core/jni/android/graphics/pdf/PdfDocument.cpp b/core/jni/android/graphics/pdf/PdfDocument.cpp
index abc3599..e2dc52b 100644
--- a/core/jni/android/graphics/pdf/PdfDocument.cpp
+++ b/core/jni/android/graphics/pdf/PdfDocument.cpp
@@ -21,7 +21,6 @@
 
 #include "CreateJavaOutputStreamAdaptor.h"
 
-#include "SkColorSpaceXformCanvas.h"
 #include "SkDocument.h"
 #include "SkPicture.h"
 #include "SkPictureRecorder.h"
@@ -95,10 +94,7 @@
 
             SkCanvas* canvas = document->beginPage(page->mWidth, page->mHeight,
                     &(page->mContentRect));
-            std::unique_ptr<SkCanvas> toSRGBCanvas =
-                    SkCreateColorSpaceXformCanvas(canvas, SkColorSpace::MakeSRGB());
-
-            toSRGBCanvas->drawPicture(page->mPicture);
+            canvas->drawPicture(page->mPicture);
 
             document->endPage();
         }
@@ -131,7 +127,7 @@
     PdfDocument* document = reinterpret_cast<PdfDocument*>(documentPtr);
     SkCanvas* canvas = document->startPage(pageWidth, pageHeight,
             contentLeft, contentTop, contentRight, contentBottom);
-    return reinterpret_cast<jlong>(Canvas::create_canvas(canvas, Canvas::XformToSRGB::kDefer));
+    return reinterpret_cast<jlong>(Canvas::create_canvas(canvas));
 }
 
 static void nativeFinishPage(JNIEnv* env, jobject thiz, jlong documentPtr) {
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 1b14d282..59ca050 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -36,6 +36,7 @@
 #include <hwbinder/ProcessState.h>
 #include <nativehelper/ScopedLocalRef.h>
 #include <vintf/parse_string.h>
+#include <utils/misc.h>
 
 #include "core_jni_helpers.h"
 
@@ -405,6 +406,11 @@
     IPCThreadState::self()->joinThreadPool();
 }
 
+static void JHwBinder_report_sysprop_change(JNIEnv /**env*/, jobject /*clazz*/)
+{
+    report_sysprop_change();
+}
+
 static JNINativeMethod gMethods[] = {
     { "native_init", "()J", (void *)JHwBinder_native_init },
     { "native_setup", "()V", (void *)JHwBinder_native_setup },
@@ -424,6 +430,9 @@
 
     { "joinRpcThreadpool", "()V",
         (void *)JHwBinder_native_joinRpcThreadpool },
+
+    { "native_report_sysprop_change", "()V",
+        (void *)JHwBinder_report_sysprop_change },
 };
 
 namespace android {
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 31e954b..c457ab0 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -233,8 +233,9 @@
     for (;;) {
         uint32_t seq;
         InputEvent* inputEvent;
+        int32_t displayId;
         status_t status = mInputConsumer.consume(&mInputEventFactory,
-                consumeBatches, frameTime, &seq, &inputEvent);
+                consumeBatches, frameTime, &seq, &inputEvent, &displayId);
         if (status) {
             if (status == WOULD_BLOCK) {
                 if (!skipCallbacks && !mBatchedInputEventPending
@@ -311,7 +312,8 @@
                     ALOGD("channel '%s' ~ Dispatching input event.", getInputChannelName());
                 }
                 env->CallVoidMethod(receiverObj.get(),
-                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
+                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj,
+                        displayId);
                 if (env->ExceptionCheck()) {
                     ALOGE("Exception dispatching input event.");
                     skipCallbacks = true;
@@ -417,7 +419,7 @@
 
     gInputEventReceiverClassInfo.dispatchInputEvent = GetMethodIDOrDie(env,
             gInputEventReceiverClassInfo.clazz,
-            "dispatchInputEvent", "(ILandroid/view/InputEvent;)V");
+            "dispatchInputEvent", "(ILandroid/view/InputEvent;I)V");
     gInputEventReceiverClassInfo.dispatchBatchedInputEventPending = GetMethodIDOrDie(env,
             gInputEventReceiverClassInfo.clazz, "dispatchBatchedInputEventPending", "()V");
 
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
index 420ff2a..58ccef18 100644
--- a/core/jni/android_view_InputEventSender.cpp
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -39,6 +39,8 @@
 
 // Log debug messages about the dispatch cycle.
 static const bool kDebugDispatchCycle = false;
+// Display id for default(primary) display.
+static const int32_t kDefaultDisplayId = 0;
 
 static struct {
     jclass clazz;
@@ -136,6 +138,7 @@
         publishedSeq = mNextPublishedSeq++;
         status_t status = mInputPublisher.publishMotionEvent(publishedSeq,
                 event->getDeviceId(), event->getSource(),
+                kDefaultDisplayId /* TODO(multi-display): propagate display id */,
                 event->getAction(), event->getActionButton(), event->getFlags(),
                 event->getEdgeFlags(), event->getMetaState(), event->getButtonState(),
                 event->getXOffset(), event->getYOffset(),
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 3887c76..dba8194 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -108,13 +108,9 @@
      // changes its locking strategy or its use of syscalls within the
      // lazy-init critical section, its use here may become unsafe.
     if (WIFEXITED(status)) {
-      if (WEXITSTATUS(status)) {
-        ALOGI("Process %d exited cleanly (%d)", pid, WEXITSTATUS(status));
-      }
+      ALOGI("Process %d exited cleanly (%d)", pid, WEXITSTATUS(status));
     } else if (WIFSIGNALED(status)) {
-      if (WTERMSIG(status) != SIGKILL) {
-        ALOGI("Process %d exited due to signal (%d)", pid, WTERMSIG(status));
-      }
+      ALOGI("Process %d exited due to signal (%d)", pid, WTERMSIG(status));
       if (WCOREDUMP(status)) {
         ALOGI("Process %d dumped core.", pid);
       }
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 5914f56..76e683f 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -31,6 +31,7 @@
 import "frameworks/base/core/proto/android/service/print.proto";
 import "frameworks/base/core/proto/android/providers/settings.proto";
 import "frameworks/base/core/proto/android/os/kernelwake.proto";
+import "frameworks/base/core/proto/android/os/procrank.proto";
 
 package android.os;
 
@@ -53,7 +54,7 @@
     //SystemProperties system_properties = 1000;
 
     // Linux services
-    //Procrank procrank = 2000;
+    Procrank procrank = 2000;
     //PageTypeInfo page_type_info = 2001;
     KernelWakeSources kernel_wake_sources = 2002;
 
diff --git a/core/proto/android/os/kernelwake.proto b/core/proto/android/os/kernelwake.proto
index 6ab1d29..e0b62aa 100644
--- a/core/proto/android/os/kernelwake.proto
+++ b/core/proto/android/os/kernelwake.proto
@@ -26,6 +26,7 @@
     repeated WakeupSourceProto wakeup_sources = 1;
 }
 
+// Next Tag: 11
 message WakeupSourceProto {
     // Name of the event which triggers application processor
     string name = 1;
diff --git a/core/proto/android/os/procrank.proto b/core/proto/android/os/procrank.proto
new file mode 100644
index 0000000..c7dbf4d
--- /dev/null
+++ b/core/proto/android/os/procrank.proto
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_outer_classname = "ProcrankProto";
+
+package android.os;
+
+//Memory usage of running processes
+message Procrank {
+    // Currently running process
+    repeated ProcessProto processes = 1;
+
+    // Summary
+    SummaryProto summary = 2;
+}
+
+// Next Tag: 11
+message ProcessProto {
+    // ID of the process
+    int32 pid = 1;
+
+    // virtual set size, unit KB
+    int64 vss = 2;
+
+    // resident set size, unit KB
+    int64 rss = 3;
+
+    // proportional set size, unit KB
+    int64 pss = 4;
+
+    // unique set size, unit KB
+    int64 uss = 5;
+
+    // swap size, unit KB
+    int64 swap = 6;
+
+    // proportional swap size, unit KB
+    int64 pswap = 7;
+
+    // unique swap size, unit KB
+    int64 uswap = 8;
+
+    // zswap size, unit KB
+    int64 zswap = 9;
+
+    // process command
+    string cmdline = 10;
+}
+
+// Next Tag: 3
+message SummaryProto {
+    ProcessProto total = 1;
+
+    ZramProto zram = 2;
+
+    RamProto ram = 3;
+}
+
+// TODO: sync on how to use these values
+message ZramProto {
+    string raw_text = 1;
+}
+
+message RamProto {
+    string raw_text = 1;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d22e83a..2652930 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2556,7 +2556,6 @@
     <!-- Allows applications to get the installed and enabled print services.
          @hide
          @SystemApi
-         @TestApi
          <p>Protection level: signature|preinstalled
     -->
     <permission android:name="android.permission.READ_PRINT_SERVICES"
diff --git a/core/res/res/drawable/toast_frame.xml b/core/res/res/drawable/toast_frame.xml
index 053b4f4..d57bd6a 100644
--- a/core/res/res/drawable/toast_frame.xml
+++ b/core/res/res/drawable/toast_frame.xml
@@ -17,8 +17,8 @@
 -->
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
-    <!-- background is material_grey_300 with .9 alpha -->
-    <solid android:color="#E6E0E0E0" />
+    <!-- background is material_grey_200 with .9 alpha -->
+    <solid android:color="#E6EEEEEE" />
     <corners android:radius="22dp" />
 </shape>
 
diff --git a/core/res/res/layout/transient_notification.xml b/core/res/res/layout/transient_notification.xml
index 2c08bf7..db586ec 100644
--- a/core/res/res/layout/transient_notification.xml
+++ b/core/res/res/layout/transient_notification.xml
@@ -34,8 +34,6 @@
         android:layout_gravity="center_horizontal"
         android:textAppearance="@style/TextAppearance.Toast"
         android:textColor="@color/primary_text_default_material_light"
-        android:shadowColor="#BB000000"
-        android:shadowRadius="2.75"
         />
 
 </LinearLayout>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index bc1f324..c310246 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Om oproepe te maak en boodskappe oor Wi-Fi te stuur, vra eers jou diensverskaffer om hierdie diens op te stel. Skakel Wi-Fi-oproepe dan weer in Instellings aan. (Foutkode: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Registreer by jou diensverskaffer"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi-oproep"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB vir MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Gekoppel aan \'n USB-toebehoorsel"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Tik vir meer opsies."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Oudiobykomstigheid word nie gesteun nie"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tik vir meer inligting"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-ontfouter gekoppel"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Tik om USB-ontfouting te deaktiveer."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Kies om USB-ontfouting te deaktiveer."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 52721b6..56931c2 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"በWi-Fi ላይ ጥሪዎችን ለማድረግ እና መልዕክቶችን ለመላክ መጀመሪያ የአገልግሎት አቅራቢዎ ይህን አገልግሎት እንዲያዘጋጅልዎ መጠየቅ አለብዎት። ከዚያ ከቅንብሮች ሆነው እንደገና የWi-Fi ጥሪን ያብሩ። (የስህተት ኮድ፦ <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"የአገልግሎት አቅራቢዎ ጋር ይመዝገቡ"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"የ%s Wi-Fi ጥሪ"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"ዩኤስቢ ለMIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"ለUSB ተቀጥላ ተያይዟል"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"ለተጨማሪ አማራጮች መታ ያድርጉ።"</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"የኦዲዮ መለዋወጫ አይደገፍም"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"ተጨማሪ መረጃ ለማግኘት መታ ያድርጉ"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB አድስ ተያይዟል"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"የዩኤስቢ ማረሚያን ለማሰናከል መታ ያድርጉ።"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ማረሚያ ላለማንቃት ምረጥ።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index be38177..618de5c 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -121,9 +121,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"‏لإجراء مكالمات وإرسال رسائل عبر Wi-Fi، اطلب من مشغّل شبكة الجوّال أولاً إعداد هذه الخدمة، ثم شغّل الاتصال عبر Wi-Fi مرة أخرى من خلال الإعدادات. (رمز الخطأ: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"التسجيل لدى مشغّل شبكة الجوّال"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"‏%s جارٍ الاتصال عبر Wi-Fi"</item>
@@ -1273,8 +1271,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"‏USB لـ MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"‏الاتصال بجهاز USB ملحق"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"انقر للحصول على المزيد من الخيارات."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"ملحق الصوت غير متوافق"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"انقر للحصول على مزيد من المعلومات"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"‏تم توصيل تصحيح أخطاء USB"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"‏انقر لتعطيل تصحيح أخطاء USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"‏اختيار تعطيل تصحيح أخطاء USB."</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index f3c7d9c..6aed812 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Zəng etmək və Wi-Fi üzərindən mesaj göndərmək üçün əvvəlcə operatordan bu cihazı quraşdırmağı tələb edin. Sonra Ayarlardan Wi-Fi zəngini deaktiv edin. (Xəta kodu: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Operatorla qeydiyyatdan keçin"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi Zəngi"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI üçün USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB aksesuara qoşuldu"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Əlavə seçimlər üçün tıklayın."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Audio aksesuar dəstəklənmir"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Daha çox məlumat üçün klikləyin"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB sazlama qoşuludur"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"USB debaqı deaktivasiya etmək üçün tıklayın."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USb debaqı deaktivasiya etməyi seçin."</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index 6457791..0f84d42 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -118,9 +118,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Da biste upućivali pozive i slali poruke preko Wi-Fi-ja, prvo zatražite od mobilnog operatera da vam omogući ovu uslugu. Zatim u Podešavanjima ponovo uključite Pozivanje preko Wi-Fi-ja. (kôd greške: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Registrujte se kod mobilnog operatera"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Wi-Fi pozivanje preko operatera %s"</item>
@@ -1207,8 +1205,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB za MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Povezano sa USB dodatkom"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Dodirnite za još opcija."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Dodatna oprema za audio sadržaj nije podržana"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Dodirnite za više informacija"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Otklanjanje grešaka sa USB-a je omogućeno"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Dodirnite da biste onemogućili otklanjanje grešaka sa USB-a."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Izaberite da biste onemogućili otklanjanja grešaka sa USB-a."</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 2787ae6..1008452 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -119,9 +119,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Каб рабіць выклікі і адпраўляць паведамленні па Wi-Fi, спачатку папрасіце свайго аператара наладзіць гэту паслугу. Затым зноў уключыце Wi-Fi-тэлефанію ў меню Налады. (Код памылкі: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Зарэгіструйцеся ў свайго аператара"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Wi-Fi-тэлефанія %s"</item>
@@ -1229,8 +1227,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB для MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Падключаны да USB-прылады"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Дакраніцеся, каб атрымаць іншыя параметры."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Аксесуар аўдыя не падтрымліваецца"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Націсніце, каб атрымаць дадатковую інфармацыю"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Адладка па USB падключана"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Дакраніцеся, каб адключыць адладку па USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Выберыце, каб адключыць адладку USB."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 1450d6b..fd58f86 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"За да извършвате обаждания и да изпращате съобщения през Wi-Fi, първо, помолете оператора си да настрои тази услуга. След това включете отново функцията за обаждания през Wi-Fi от настройките. (Код на грешката: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Регистриране с оператора ви"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s – обаждания през Wi-Fi"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB за MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Установена е връзка с аксесоар за USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Докоснете за още опции."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Аудиоаксесоарът не се поддържа"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Докоснете за още информация"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Отстраняване на грешки през USB"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Докоснете, за да деактивирате отстраняването на грешки през USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Изберете, за да деактивирате отстраняването на грешки през USB."</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index c180311..b82b792 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"ওয়াই-ফাই এর মাধ্যমে কল করতে ও মেসেজ পাঠাতে, প্রথমে আপনার পরিষেবা প্রদানকারীকে এই পরিষেবার সেট-আপ করতে বলুন। তারপর আবার সেটিংস থেকে ওয়াই-ফাই কলিং চালু করুন। (ত্রুটি কোড: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"আপনার পরিষেবা প্রদানকারীকে নথিভুক্ত করুন"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s ওয়াই-ফাই কলিং"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI এর জন্য USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"একটি USB যন্ত্রাংশতে সংযুক্ত হয়েছে"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"আরো বিকল্পের জন্য আলতো চাপুন৷"</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"অডিও অ্যাক্সেসরি সমর্থিত নয়"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"আরও তথ্যের জন্য ট্যাপ করুন"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB ডিবাগিং সংযুক্ত হয়েছে"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"USB ডিবাগিং অক্ষম করতে আলতো চাপুন৷"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ডিবাগিং অক্ষম করতে বেছে নিন।"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 805d3e4..c62d848 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -118,9 +118,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Da biste pozivali i slali poruke koristeći Wi-Fi mrežu, prvo zatražite od operatera da postavi tu uslugu. Zatim ponovo uključite Wi-Fi pozivanje u Postavkama. (Kôd greške: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Registrirajte se kod svog operatera"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Wi-Fi pozivanje preko operatera %s"</item>
@@ -1211,8 +1209,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB za MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Uspostavljena veza sa USB pohranom"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Dodirnite za više opcija."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Audio pribor nije podržan"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Dodirnite za više informacija"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Otklanjanje grešaka putem uređaja spojenog na USB je uspostavljeno"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Dodirnite da onemogućite otklanjanje grešaka putem uređaja spojenog na USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Odaberite da onemogućite ispravljanje grešaka koristeći USB"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 242193a..489a8d0 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Per fer trucades i enviar missatges per Wi-Fi, primer has de demanar a l\'operador de telefonia mòbil que configuri aquest servei. Després, torna a activar les trucades per Wi-Fi a Configuració. (Codi d\'error: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Registra\'t amb el teu operador de telefonia mòbil"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Trucada de Wi-Fi de: %s"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB per a MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connectat a un accessori USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Toca per veure més opcions."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"L\'accessori d\'àudio no és compatible"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Toca per obtenir més informació"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Depuració USB activada"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Toca per desactivar la depuració USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecciona per desactivar la depuració USB"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 250bfeb..69f55ea 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -119,9 +119,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Chcete-li volat a odesílat SMS přes síť Wi-Fi, nejprve požádejte operátora, aby vám tuto službu nastavil. Poté volání přes Wi-Fi opět zapněte v Nastavení. (Kód chyby: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Registrace u operátora"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Volání přes Wi-Fi: %s"</item>
@@ -1229,8 +1227,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB v režimu MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Připojeno k perifernímu zařízení USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Klepnutím zobrazíte další možnosti."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Zvukové příslušenství není podporováno"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Klepnutím zobrazíte další informace"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Ladění přes USB připojeno"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Klepnutím zakážete ladění USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Vyberte, chcete-li zakázat ladění USB."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 8c4deaf..9361022 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Hvis du vil foretage opkald og sende beskeder via Wi-Fi, skal du først anmode dit mobilselskab om at konfigurere denne tjeneste. Derefter skal du aktivere Wi-Fi-opkald igen fra Indstillinger. (Fejlkode: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Registrer dig hos dit mobilselskab"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi-opkald"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB til MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Tilsluttet et USB-ekstraudstyr"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Tryk for at se flere muligheder."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Lydtilbehøret understøttes ikke"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tryk for at få flere oplysninger"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-fejlretning er tilsluttet"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Tryk for at deaktivere fejlretning via USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Vælg for at deaktivere USB-fejlretning."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index bbbd1ee..a2afc38 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Um über WLAN telefonieren und Nachrichten senden zu können, bitte zuerst deinen Mobilfunkanbieter, diesen Dienst einzurichten. Aktiviere die Option \"Anrufe über WLAN\" dann noch einmal über die Einstellungen. (Fehlercode: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Registriere dich bei deinem Mobilfunkanbieter."</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Anrufe über WLAN"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB für MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Mit USB-Zubehör verbunden"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Für weitere Optionen tippen."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Audiozubehör wird nicht unterstützt"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tippen, um weitere Informationen zu erhalten"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-Debugging aktiviert"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Zum Deaktivieren von USB-Debugging tippen"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB-Debugging deaktivieren: auswählen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index acf95cd..c9d9def 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Για να κάνετε κλήσεις και να στέλνετε μηνύματα μέσω Wi-Fi, ζητήστε πρώτα από την εταιρεία κινητής τηλεφωνίας να ρυθμίσει την υπηρεσία. Στη συνέχεια, ενεργοποιήστε ξανά την Κλήση Wi-Fi από τις Ρυθμίσεις. (Κωδικός σφάλματος: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Εγγραφείτε μέσω της εταιρείας κινητής τηλεφωνίας"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Κλήση Wi-Fi"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB για MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Σύνδεση σε αξεσουάρ USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Πατήστε για περισσότερες επιλογές."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Το εξάρτημα ήχου δεν υποστηρίζεται"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Πατήστε για να δείτε περισσότερες πληροφορίες"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Συνδέθηκε ο εντοπισμός σφαλμάτων USB"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Πατήστε για απενεργοποίηση του εντοπισμού σφαλμάτων USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Επιλογή για απενεργοποίηση του εντοπισμού σφαλμάτων USB."</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index d0f9c3f..a5e4d9d 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"To make calls and send messages over Wi-Fi, first ask your operator to set up this service. Then turn on Wi-Fi calling again from Settings. (Error code: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Register with your operator"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi Calling"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB for MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connected to a USB accessory"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Tap for more options."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Audio accessory not supported"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tap for more info"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB debugging connected"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Tap to disable USB debugging."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Select to disable USB debugging."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index d0f9c3f..a5e4d9d 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"To make calls and send messages over Wi-Fi, first ask your operator to set up this service. Then turn on Wi-Fi calling again from Settings. (Error code: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Register with your operator"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi Calling"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB for MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connected to a USB accessory"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Tap for more options."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Audio accessory not supported"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tap for more info"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB debugging connected"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Tap to disable USB debugging."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Select to disable USB debugging."</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index d0f9c3f..a5e4d9d 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"To make calls and send messages over Wi-Fi, first ask your operator to set up this service. Then turn on Wi-Fi calling again from Settings. (Error code: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Register with your operator"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi Calling"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB for MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connected to a USB accessory"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Tap for more options."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Audio accessory not supported"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tap for more info"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB debugging connected"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Tap to disable USB debugging."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Select to disable USB debugging."</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index bb93867..7b0508a 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Para hacer llamadas y enviar mensajes mediante Wi-Fi, solicítale a tu proveedor que configure este servicio. Luego, vuelve a activar la Llamada con Wi-Fi en Configuración. (código de error: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Regístrate con tu proveedor."</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Llamada por Wi-Fi de %s"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Conectado a un accesorio USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Presiona para ver más opciones."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"El accesorio de audio no es compatible"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Presiona para obtener más información"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Depuración por USB conectada"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Presiona para inhabilitar la depuración por USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Seleccionar para desactivar la depuración por USB"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 1766e89..056379e 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Para hacer llamadas y enviar mensajes por Wi-Fi, pide antes a tu operador que configure este servicio. Una vez hecho esto, vuelva a activar la llamada por Wi-Fi en Ajustes. (Código de error: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Regístrate con tu operador"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Llamada Wi-Fi de %s"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Conectado a un accesorio USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Toca para ver más opciones."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Accesorio de audio no compatible"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Toca para obtener más información"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Depuración USB habilitada"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Toca para inhabilitar la depuración USB"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Seleccionar para inhabilitar la depuración USB"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 04a6f60..e2a8e94 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"WiFi-võrgu kaudu helistamiseks ja sõnumite saatmiseks paluge operaatoril esmalt see teenus seadistada. Seejärel lülitage WiFi-kõned menüüs Seaded uuesti sisse. (Veakood: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Registreeruge operaatori juures"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s WiFi kaudu helistamine"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB MIDI jaoks"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Ühendatud USB-lisaseadmega"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Puudutage lisavalikute nägemiseks."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Helitarvikut ei toetata"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Puudutage lisateabe saamiseks"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-silumine ühendatud"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Puudutage USB-silumise keelamiseks."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Valige USB silumise keelamiseks"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index d9ff28a..6b90868 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Wi-Fi bidez deiak egiteko eta mezuak bidaltzeko, eskatu operadoreari zerbitzu hori gaitzeko. Ondoren, aktibatu Wi-Fi bidezko deiak Ezarpenak atalean. (Errore-kodea: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Erregistratu operadorearekin"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi bidezko deiak"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI modurako USBa"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB osagarri batera konektatuta"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Sakatu aukera gehiago ikusteko."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Ez da onartzen audio-osagarri hori"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Informazio gehiago lortzeko, sakatu hau"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB arazketa konektatuta"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Sakatu USB arazketa desgaitzeko."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Hautatu USB arazketa desgaitzeko."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 0a87a53f..f3b39f3 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"‏برای برقراری تماس و ارسال پیام ازطریق Wi-Fi، ابتدا از شرکت مخابراتی خود بخواهید این سرویس را تنظیم کند. سپس در «تنظیمات»۷ دوباره «تماس ازطریق Wi-Fi» را روشن کنید. (کد خطا: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"ثبت‌نام با شرکت مخابراتی شما"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"‏تماس ‪%s Wi-Fi"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"‏USB برای MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"‏به یک وسیله جانبی USB وصل شده است"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"برای گزینه‌های بیشتر ضربه بزنید."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"وسیله جانبی صوتی پشتیبانی نمی‌شود"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"برای اطلاعات بیشتر ضربه بزنید"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"‏اشکال‌زدایی USB متصل شد"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"‏برای غیرفعال کردن اشکال‌زدایی USB ضربه بزنید."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"‏انتخاب کنید تا رفع عیب USB غیرفعال شود."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index d38ae1d..9ba1ae2 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Jos haluat soittaa puheluita ja lähettää viestejä Wi-Fin kautta, pyydä ensin operaattoriasi ottamaan tämä palvelu käyttöön. Ota sitten Wi-Fi-puhelut käyttöön asetuksissa. (Virhekoodi: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Rekisteröidy operaattorisi asiakkaaksi."</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Wi-Fi-puhelut: %s"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB on MIDI-tilassa"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Liitetty USB-laitteeseen"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Näet lisää vaihtoehtoja napauttamalla."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Äänilisälaitetta ei tueta."</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Saat lisätietoja napauttamalla."</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-vianetsintä yhdistetty"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Poista USB-vianetsintä käytöstä napauttamalla."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Poista USB-vianetsintä käytöstä valitsemalla tämä."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 919b93d..db1517e 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Pour effectuer des appels et envoyer des messages par Wi-Fi, demandez tout d\'abord à votre fournisseur de services de configurer ce service. Réactivez ensuite les appels Wi-Fi dans les paramètres. (Code d\'erreur : <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Inscrivez-vous auprès de votre fournisseur de services"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Appels Wi-Fi %s"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB pour MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connecté à un accessoire USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Touchez pour afficher plus d\'options."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Accessoire audio non pris en charge"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Touchez l\'écran pour obtenir plus d\'information"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Débogage USB activé"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Touchez pour désactiver le débogage USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Sélectionnez cette option pour désactiver le débogage USB."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 2aad91b..8bdb297 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Pour passer des appels et envoyer des messages via le Wi-Fi, demandez d\'abord à votre opérateur de configurer ce service. Ensuite, réactivez les appels Wi-Fi dans les paramètres. (Code d\'erreur : <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Inscrivez-vous auprès de votre opérateur."</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Appels Wi-Fi %s"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB en mode MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Connecté à un accessoire USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Appuyez ici pour plus d\'options."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Accessoire audio non compatible"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Appuyer pour afficher plus d\'informations"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Débogage USB activé"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Appuyez ici pour désactiver le débogage USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Sélectionnez cette option pour désactiver le débogage USB."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 715a1b0..37b8dc7 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Para facer chamadas e enviar mensaxes a través da wifi, primeiro solicítalle ao operador que configure este servizo. Despois, activa de novo as chamadas por wifi en Configuración. (Código de erro: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Rexístrate co teu operador"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Chamadas wifi de %s"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Conectado a un accesorio USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Toca para ver máis opcións."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"O accesorio de audio non é compatible"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Toca para obter máis información"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Depuración USB conectada"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Toca para desactivar a depuración de erros de USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecciona a opción para desactivar a depuración de USB."</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 881e860..93403ab 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Wi-Fi પરથી કૉલ કરવા અને સંદેશા મોકલવા માટે પહેલા તમારા કૅરિઅરને આ સેવા સેટ કરવા માટે કહો. પછી સેટિંગ્સમાંથી Wi-Fi કૉલિંગ ફરીથી ચાલુ કરો. (ભૂલ કોડ: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"તમારા કેરીઅર સાથે નોંધણી કરો"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi કૉલિંગ"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI માટે USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB ઍક્સેસરીથી કનેક્ટ થયાં"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"વધુ વિકલ્પો માટે ટૅપ કરો."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"ઑડિયો ઍક્સેસરી સમર્થિત નથી"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"વધુ માહિતી માટે ટૅપ કરો"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB ડીબગિંગ કનેક્ટ થયું."</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"USB ડીબગિંગ અક્ષમ કરવા માટે ટૅપ કરો."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ડિબગીંગને અક્ષમ કરવા માટે પસંદ કરો."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 7d69c11..a911aa1 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"वाई-फ़ाई से कॉल करने और संदेश भेजने के लिए, सबसे पहले अपने वाहक से इस सेवा को सेट अप करने के लिए कहें. उसके बाद सेटिंग से वाई-फ़ाई कॉलिंग को दोबारा चालू करें. (गड़बड़ी कोड: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"अपने वाहक के साथ पंजीकृत करें"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s वाई-फ़ाई कॉलिंग"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI के लिए USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB सहायक सामग्री से कनेक्‍ट कि‍या गया"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"अधिक विकल्पों के लिए टैप करें."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"ऑडियो एक्सेसरी इस पर काम नहीं करती है"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"अधिक जानकारी के लिए टैप करें"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB डीबग कनेक्ट किया गया"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"USB डीबग करना अक्षम करने के लिए टैप करें."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB डीबग करना अक्षम करने के लिए चुनें."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 7acea32..55f7579 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -118,9 +118,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Da biste telefonirali i slali poruke putem Wi-Fi-ja, od mobilnog operatera morate tražiti da postavi tu uslugu. Zatim ponovo uključite Wi-Fi pozive u postavkama. (Kôd pogreške: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Registrirajte se kod mobilnog operatera"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi pozivanje"</item>
@@ -1207,8 +1205,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB za MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Spojen na USB pribor"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Dodirnite za više opcija."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Audiododatak nije podržan"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Dodirnite za više informacija"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Priključen je alat za otklanjanje pogrešaka USB-om"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Dodirnite da biste onemogućili otklanjanje pogrešaka putem USB-a."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Odaberite da biste onemogućili rješavanje programske pogreške na USB-u."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index d71d115..227c208 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Ha Wi-Fi-hálózaton szeretne telefonálni és üzenetet küldeni, kérje meg szolgáltatóját, hogy állítsa be ezt a szolgáltatást. Ezután kapcsolja be újra a Wi-Fi-hívást a Beállításokban. (Hibakód: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Regisztráljon a szolgáltatójánál"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi-hívás"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB MIDI-hez"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Csatlakoztatva egy USB-kiegészítőhöz"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Koppintson a további beállítások megjelenítéséhez."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Ez az audiotartozék nem támogatott"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Koppintson, ha további információra van szüksége"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB hibakereső csatlakoztatva"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Koppintson az USB fejlesztő mód kikapcsolásához."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Válassza ezt az USB hibakeresés kikapcsolásához."</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 4328244..65bb856 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Wi-Fi-ի միջոցով զանգեր կատարելու և հաղորդագրություններ ուղարկելու համար նախ դիմեք ձեր օպերատորին՝ այս ծառայությունը կարգավորելու համար: Այնուհետև նորից միացրեք «Զանգեր Wi-Fi-ի միջոցով» ընտրանքը Կարգավորումներից: (Սխալի կոդ՝ <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Գրանցվեք օպերատորի մոտ"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi զանգեր"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI-ի USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Կապակցված է USB լրասարքի"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Հպեք՝ լրացուցիչ ընտրանքների համար:"</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Աուդիո սարքը չի աջակցվում"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Հպեք՝ լրացուցիչ տեղեկություններ ստանալու համար"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB վրիպազերծումը միացված է"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Հպեք՝ USB վրիպազերծումն անջատելու համար:"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Ընտրել` USB կարգաբերումը կասեցնելու համար:"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index f684644..b325314 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Untuk menelepon dan mengirim pesan melalui Wi-Fi, tanyalah ke operator Anda terlebih dahulu untuk menyiapkan layanan ini. Kemudian, aktifkan kembali panggilan Wi-Fi dari Setelan. (Kode error: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Harap daftarkan ke operator"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Panggilan Wi-Fi"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB untuk MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Tersambung ke aksesori USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Ketuk untuk opsi lainnya."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Aksesori audio tidak didukung"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tap untuk info selengkapnya"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Debugging USB terhubung"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Ketuk untuk menonaktifkan debug USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Pilih untuk menonaktifkan debugging USB."</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index dc5752b..281b59c 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Til að hringja og senda skilaboð yfir Wi-Fi þarftu fyrst að biðja símafyrirtækið þitt um að setja þá þjónustu upp. Kveiktu síðan á Wi-Fi símtölum í stillingunum. (Villukóði: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Skráðu þig hjá símafyrirtækinu"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi símtöl"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB fyrir MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Tengt við USB-aukabúnað"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Ýttu til að sjá fleiri valkosti."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Hljóðaukabúnaður er ekki studdur"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Ýttu til að fá frekari upplýsingar"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-villuleit tengd"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Ýttu til að slökkva á USB-villuleit."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Veldu til að gera USB-villuleit óvirka."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 8a52cbe..91f1459 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Per effettuare chiamate e inviare messaggi tramite Wi-Fi, chiedi prima al tuo operatore di impostare questo servizio. Dopodiché, attiva di nuovo la funzione Chiamate Wi-Fi nelle impostazioni. (Codice di errore: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Registrati con il tuo operatore"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Chiamata Wi-Fi %s"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB per la modalità MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Collegato a un accessorio USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Tocca per altre opzioni."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Accessorio audio non supportato"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tocca per ulteriori informazioni"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Debug USB collegato"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Tocca per disattivare il debug USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Seleziona per disattivare il debug USB."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index d32f972..92f032b 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -119,9 +119,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"‏כדי להתקשר ולשלוח הודעות ברשת Wi-Fi, תחילה יש לבקש מהספק להגדיר את השירות. לאחר מכן, יש להפעיל שוב שיחות Wi-Fi ב\'הגדרות\'. (קוד שגיאה: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"הירשם אצל הספק"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"‏שיחות Wi-Fi של %s"</item>
@@ -1229,8 +1227,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"‏USB ל-MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"‏מחובר לאביזר USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"הקש לקבלת אפשרויות נוספות."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"אביזר אודיו אינו נתמך"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"הקש לקבלת מידע נוסף"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"‏ניפוי באגים של USB מחובר"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"‏הקש כדי להשבית ניפוי באגים של USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"‏בחר להשבית ניפוי באגים ב-USB."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 1f3b2b6..2f68c37 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -118,7 +118,7 @@
     <item msgid="3910386316304772394">"Wi-Fi 経由で音声通話の発信やメッセージの送信を行うには、携帯通信会社に Wi-Fi サービスを申し込んだ上で、設定画面で Wi-Fi 通話を再度 ON にしてください(エラーコード: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"携帯通信会社に登録してください"</item>
+    <item msgid="7472393097168811593">"携帯通信会社に登録してください(エラーコード: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
@@ -1185,8 +1185,8 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USBをMIDIに使用"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USBアクセサリを接続しました"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"タップしてその他のオプションを表示します。"</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"オーディオ アクセサリがサポートされていません"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"タップして詳細をご確認ください"</string>
+    <string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"アナログのオーディオ アクセサリを検出"</string>
+    <string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"接続したデバイスはこのスマートフォンと互換性がありません。タップすると、詳細を確認できます。"</string>
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USBデバッグが接続されました"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"タップして USB デバッグを無効にします。"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USBデバッグを無効にする場合に選択します。"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index ec826d7..d222a84 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Wi-Fi-ს მეშვეობით ზარების განსახორციელებლად ან შეტყობინებების გასაგზავნად, პირველ რიგში, ამ სერვისის გააქტიურება თქვენს ოპერატორს უნდა თხოვოთ. შემდეგ კი ხელახლა ჩართეთ Wi-Fi დარეკვა პარამეტრებიდან. (შეცდომის კოდი: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"დაარეგისტრირეთ თქვენი ოპერატორი"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s დარეკვა Wi-Fi-ს მეშვეობით"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB MIDI-სთვის"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"დაკავშირებულია USB აქსესუართან"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"შეეხეთ დამატებითი ვარიანტების სანახავად."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"აუდიო აქსესუარი მხარდაუჭერელია"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"შეეხეთ დამატებითი ინფორმაციისთვის"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB გამართვა შეერთებულია"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"შეეხეთ USB-გამართვის გასათიშად."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"მონიშნეთ რათა შეწყვიტოთ USB-ის გამართვა"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index eb307d9..5bab572 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Wi-Fi арқылы қоңырау шалу немесе хабарлар жіберу үшін, алдымен операторыңыздан құрылғыны реттеуді сұраңыз. Содан кейін \"Параметрлер\" бөлімінен Wi-Fi қоңырауларын қайта қосыңыз. (Қате коды: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Жабдықтаушыңыз арқылы тіркелу"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi арқылы қоңырау шалу"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI режиміне арналған USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB жабдығына қосылған"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Қосымша опциялар үшін түртіңіз."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Аудио аксессуарға қолдау көрсетілмейді"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Қосымша ақпарат алу үшін түртіңіз"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB жөндеу қосылған"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"USB түзетуін өшіру үшін түртіңіз."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB түзетуін өшіру үшін таңдаңыз."</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index a1f14f5..4260a08 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -118,7 +118,7 @@
     <item msgid="3910386316304772394">"ដើម្បីហៅទូរសព្ទ និងផ្ញើសារតាម Wi-Fi អ្នកត្រូវស្នើឲ្យក្រុមហ៊ុនបម្រើសេវាទូរសព្ទរបស់អ្នកដំឡើងសេវាកម្មនេះជាមុនសិន។ បន្ទាប់មកបើកការហៅតាម Wi-Fi ម្តងទៀតនៅក្នុងការកំណត់។ (លេខកូដបញ្ហា៖ <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"ចុះឈ្មោះជាមួយក្រុមហ៊ុនរបស់អ្នក"</item>
+    <item msgid="7472393097168811593">"ចុះឈ្មោះ​ជាមួយ​ក្រុមហ៊ុន​បម្រើសេវា​ទូរសព្ទរបស់អ្នក (លេខកូដ​មានបញ្ហា៖ <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
@@ -1187,8 +1187,8 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB សម្រាប់ MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"បាន​ភ្ជាប់​ឧបករណ៍​យូអេសប៊ី"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"ប៉ះសម្រាប់ជម្រើសជាច្រើនទៀត"</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"មិនស្គាល់គ្រឿងបរិក្ខារសំឡេងទេ"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"ចុចដើម្បីទទួលព័ត៌មានបន្ថែម"</string>
+    <string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"បាន​រកឃើញ​គ្រឿង​បរិក្ខារ​សំឡេង​អាណាឡូក"</string>
+    <string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"ឧបករណ៍​ដែលភ្ជាប់​មក​ជាមួយ​មិនត្រូវគ្នា​ជាមួយ​ទូរសព្ទ​នេះទេ។ ចុច​ដើម្បី​ស្វែងយល់​បន្ថែម។"</string>
     <string name="adb_active_notification_title" msgid="6729044778949189918">"បាន​ភ្ជាប់​ការ​កែ​កំហុស​យូអេសប៊ី"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"ប៉ះដើម្បីបិទដំណើរការកែកំហុសយូអេសប៊ី"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"ជ្រើស​ ដើម្បី​បិទ​ការ​កែ​កំហុស​យូអេសប៊ី។"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 41f4c6f..2144e16 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"ವೈ-ಫೈ ಮೂಲಕ ಕರೆಗಳನ್ನು ಮಾಡಲು ಮತ್ತು ಸಂದೇಶಗಳನ್ನು ಕಳುಹಿಸಲು, ಈ ಸೇವೆಯನ್ನು ಹೊಂದಿಸಲು ಮೊದಲು ನಿಮ್ಮ ವಾಹಕವನ್ನು ಕೇಳಿ. ಆ ನಂತರ ಸೆಟ್ಟಿಂಗ್‌ಗಳಿಂದ ವೈ-ಫೈ ಕರೆಮಾಡುವಿಕೆಯನ್ನು ಅನ್ನು ಆನ್ ಮಾಡಿ. (ದೋಷ ಕೋಡ್: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"ನಿಮ್ಮ ವಾಹಕದಲ್ಲಿ ನೋಂದಾಯಿಸಿಕೊಳ್ಳಿ"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s ವೈ-ಫೈ ಕರೆ ಮಾಡುವಿಕೆ"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI ಗೆ USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB ಪರಿಕರಕ್ಕೆ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"ಹೆಚ್ಚಿನ ಆಯ್ಕೆಗಳಿಗೆ ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"ಆಡಿಯೋ ಪರಿಕರಗಳು ಬೆಂಬಲಿತವಾಗಿಲ್ಲ"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"ಹೆಚ್ಚಿನ ಮಾಹಿತಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB ಡೀಬಗಿಂಗ್‌‌ ಸಂಪರ್ಕ"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"USB ಡೀಬಗ್‌ ಮಾಡುವಿಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ಡೀಬಗ್‌ ಮಾಡುವಿಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲು ಆಯ್ಕೆ ಮಾಡಿ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index bccf10d..60eaee2 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Wi-Fi를 사용하여 전화를 걸고 메시지를 보내려면 먼저 이동통신사에 문의하여 서비스를 설정해야 합니다. 그런 다음 설정에서 Wi-Fi 통화를 사용 설정하시기 바랍니다. (오류 코드: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"이동통신사에 등록"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi 통화"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI용 USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB 액세서리에 연결됨"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"옵션을 더 보려면 탭하세요."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"지원되지 않는 오디오 액세서리"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"자세한 내용은 탭하여 확인하세요."</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB 디버깅 연결됨"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"USB 디버깅을 사용하지 않으려면 탭하세요."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB 디버깅을 사용하지 않으려면 선택합니다."</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index dda493e..5254b29 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Wi-Fi аркылуу чалууларды аткарып жана билдирүүлөрдү жөнөтүү үчүн адегенде байланыш операторуңуздан бул кызматты орнотушун сураныңыз. Андан соң, Жөндөөлөрдөн Wi-Fi чалууну кайра күйгүзүңүз. (Ката коду: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Операторуңузга катталыңыз"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi Чалуу"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI үчүн USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB аксессуарга байланышты"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Кошумча параметрлерди ачуу үчүн таптап коюңуз."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Аудио шайманы колдоого алынбайт"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Кеңири маалымат алуу үчүн таптап коюңуз"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Мүчүлүштүктөрдү USB аркылуу оңдоо иштетилген"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Мүчүлштктрдү USB аркл оңдну өчр үчн тийп коюңуз."</string>
     <!-- no translation found for adb_active_notification_message (8470296818270110396) -->
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 75db16b..49baec3 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"ເພື່ອໂທ ແລະ ສົ່ງຂໍ້ຄວາມຜ່ານ Wi-Fi, ໃຫ້ແຈ້ງໃຫ້ຜູ້ໃຫ້ບໍລິການຂອງທ່ານຕັ້ງບໍລິການນີ້. ຈາກນັ້ນເປີດໃຊ້ການໂທ Wi-Fi ອີກຄັ້ງຈາກການຕັ້ງຄ່າ. (ລະຫັດຂໍ້ຜິດພາດ: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"ລົງ​ທະ​ບຽນ​ກັບ​ຜູ້​ໃຫ້​ບໍ​ລິ​ການ​ເຄືອ​ຂ່າຍ​ຂອງ​ທ່ານ"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"ການ​ໂທ %s Wi-Fi"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB ສຳ​ລັບ MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"ເຊື່ອມຕໍ່ກັບອຸປະກອນເສີມ USB ແລ້ວ"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"ແຕະເພື່ອເບິ່ງຕົວເລືອກເພີ່ມເຕີມ."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"ບໍ່ຮອງຮັບອຸປະກອນເສີມສຽງ"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"ແຕະເພື່ອເບິ່ງຂໍ້ມູນ"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"ເຊື່ອມຕໍ່ການດີບັ໊ກຜ່ານ USB ແລ້ວ"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"ແຕະເພື່ອປິດການດີບັກຜ່ານ USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"ເລືອກເພື່ອປິດການດີບັ໊ກຜ່ານ USB."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 83829dc..86c0e29 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -119,9 +119,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Jei norite skambinti ir siųsti pranešimus naudodami „Wi-Fi“, pirmiausia paprašykite operatoriaus nustatyti šią paslaugą. Tada vėl įjunkite „Wi-Fi“ skambinimą skiltyje „Nustatymai“. (Klaidos kodas: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Užregistruokite pas operatorių"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"„%s“ „Wi-Fi“ skambinimas"</item>
@@ -1229,8 +1227,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB (MIDI)"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Prijungta prie USB priedo"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Palieskite, kad būtų rodoma daugiau parinkčių."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Garso įrašo priedas nepalaikomas"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Palieskite, kad sužinotumėte daugiau informacijos"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB derinimas prijungtas"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Palieskite, kad išjungtumėte USB derinimą."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Pasirinkite, kas išjungtumėte USB derinimą."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 159c7d1..d1779e5 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -118,9 +118,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Lai veiktu zvanus un sūtītu īsziņas Wi-Fi tīklā, vispirms lūdziet mobilo sakaru operatoram iestatīt šo pakalpojumu. Pēc tam iestatījumos vēlreiz ieslēdziet Wi-Fi zvanus. (Kļūdas kods: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Reģistrēt to pie sava mobilo sakaru operatora"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi zvani"</item>
@@ -1207,8 +1205,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB savienojums MIDI režīmā"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Ir izveidots savienojums ar USB piederumu."</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Pieskarieties, lai skatītu citas iespējas."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Audio piederums netiek atbalstīts"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Pieskarieties, lai uzzinātu vairāk"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB atkļūdošana ir pievienota."</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Pieskarieties, lai atspējotu USB atkļūdošanu."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Atlasiet, lai atspējotu USB atkļūdošanu."</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 5195099..5abc1dc 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"За да воспоставувате повици и да испраќате пораки преку Wi-Fi, прво побарајте од операторот да ја постави услугава. Потоа, вклучете ја повторно „Повикување преку Wi-Fi“ во „Поставки“. (Код за грешка: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Регистрирајте се со операторот"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Повикување преку Wi-Fi"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB за МИДИ"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Поврзан со USB додаток"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Допрете за повеќе опции."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Аудиододатокот не е поддржан"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Допрете за повеќе информации"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Поврзано е отстранување грешки преку USB"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Допрете за да се оневозможи отстранувањето грешки преку USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Изберете за да се оневозможи отстранување грешки на USB."</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 2a2d3cc..b7214da 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"വൈഫൈ വഴി കോളുകൾ ചെയ്യാനും സന്ദേശങ്ങൾ അയയ്‌ക്കാനും ആദ്യം നിങ്ങളുടെ കാരിയറോട് ഈ സേവനം സജ്ജമാക്കാൻ ആവശ്യപ്പെടുക. ക്രമീകരണത്തിൽ നിന്ന് വീണ്ടും വൈഫൈ കോളിംഗ് ഓണാക്കുക. (പിശക് കോഡ്: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"നിങ്ങളുടെ കാരിയറിൽ രജിസ്റ്റർ ചെയ്യുക"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s വൈഫൈ കോളിംഗ്"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI-യ്‌ക്കായുള്ള USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"ഒരു USB ആക്‌സസ്സറി കണക്റ്റുചെയ്‌തു"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"കൂടുതൽ ഓപ്ഷനുകൾക്ക് ടാപ്പുചെയ്യുക."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"ഓഡിയോ ആക്‌സസറി പ്രവർത്തിക്കുകയില്ല"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"കൂടുതൽ വിവരങ്ങൾക്ക് ടാപ്പുചെയ്യുക"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB ഡീബഗ്ഗിംഗ് കണക്‌റ്റുചെയ്‌തു"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"USB ഡീബഗ്ഗിംഗ് പ്രവർത്തനരഹിതമാക്കാൻ ടാപ്പുചെയ്യുക."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ഡീബഗ്ഗുചെയ്യൽ പ്രവർത്തനരഹിതമാക്കാൻ തിരഞ്ഞെടുക്കുക."</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 57ed0e2..27fb32d 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Wi-Fi-аар дуудлага хийх, мессеж илгээх бол эхлээд оператор компаниасаа энэ үйлчилгээг тохируулж өгөхийг хүснэ үү. Дараа нь Тохиргооноос Wi-Fi дуудлага хийх үйлдлийг асаана уу. (Алдааны код: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Операторт бүртгүүлэх"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi Дуудлага"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI-ийн USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB төхөөрөмжид холбогдов"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Бусад сонголтыг харахын тулд товшино уу."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Аудио хэрэгслийг дэмжээгүй байна"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Дэлгэрэнгүйг үзэхийн тулд товшино уу"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB дебаг холбогдсон"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"USB-н алдаа засварлахыг идэвхгүй болгохын тулд товшино уу."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB дебаг хийхийг идэвхгүй болгох бол сонгоно уу."</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 188ea9b..2209016 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"वाय-फायवरून कॉल करण्यासाठी आणि संदेश पाठवण्यासाठी आधी तुमच्या कॅरियरला ही सेवा सेट अप करण्यास सांगा. नंतर सेटिंग्जमधून वाय-फाय वापरून कॉल करणे पुन्हा चालू करा. (एरर कोड <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"आपल्या वाहकासह नोंदणी करा"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s वाय-फाय कॉलिंग"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI साठी USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB उपसाधनावर कनेक्ट केले"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"अधिक पर्यायांसाठी टॅप करा."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"ऑडिओ अॅक्‍सेसरी समर्थित नाही"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"आणखी माहितीसाठी येथे टॅप करा"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB डीबग करणे कनेक्‍ट केले"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"USB डीबग करणे अक्षम करण्यासाठी टॅप करा."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB डीबगिंग बंद करण्यासाठी निवडा."</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 154a312..679812a 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -118,7 +118,7 @@
     <item msgid="3910386316304772394">"Untuk membuat panggilan dan menghantar mesej melalui Wi-Fi, minta pembawa anda menyediakan perkhidmatan ini terlebih dahulu. Kemudian, hidupkan panggilan Wi-Fi sekali lagi daripada Tetapan. (Kod ralat: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Daftar dengan pembawa anda"</item>
+    <item msgid="7472393097168811593">"Daftar dengan pembawa anda (Kod ralat: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
@@ -1185,8 +1185,8 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB untuk MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Disambungkan kepada aksesori USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Ketik untuk mendapatkan lagi pilihan."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Aksesori audio tidak disokong"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Ketik untuk mendapatkan maklumat lanjut"</string>
+    <string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"Aksesori audio analog dikesan"</string>
+    <string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"Peranti yang disambungkan tidak serasi dengan telefon ini. Ketik untuk mengetahui lebih lanjut."</string>
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Penyahpepijatan USB disambungkan"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Ketik untuk melumpuhkan penyahpepijatan USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Pilih untuk melumpuhkan penyahpepijatan USB."</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index fad8797..233ccf8 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Wi-Fi အသုံးပြု၍ ဖုန်းခေါ်ရန်နှင့် မက်ဆေ့ဂျ်ပို့ရန်အတွက် သင့်ဝန်ဆောင်မှုပေးသူကို ဤဝန်ဆောင်မှုအား သတ်မှတ်ပေးရန် ဦးစွာတောင်းဆိုပါ။ ထို့နောက် ဆက်တင်ထဲသို့ သွား၍ Wi-Fi ဖြင့် ဖုန်းခေါ်ခြင်းကို ဖွင့်ရပါမည်။ (အမှားကုဒ်- <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"သင့် မိုဘိုင်းဝန်ဆောင်မှုဖြင့် မှတ်ပုံတင်ရန်"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi  ခေါ်ဆိုမှု"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI အတွက် USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USBတွဲဖက်ပစ္စည်းအား ချိတ်ဆက်ထားသည်"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"နောက်ထပ်ရွေးချယ်စရာများအတွက် တို့ပါ။"</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"အသံ ဆက်စပ်ပစ္စည်းကို မပံ့ပိုးပါ"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"နောက်ထပ် အချက်အလက်များအတွက် တို့ပါ"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB အမှားစစ်ခြင်းအား ချိတ်ဆက်ထားသည်"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"USB ဆက်သွယ်ရေးစနစ်ကို ပိတ်ရန် တို့ပါ။"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ဖြင့် အမှားရှာပြင်ခြင်းကို ပိတ်ရန် ရွေးပါ။"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 5413cc4..e5d6f54 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"For å ringe og sende meldinger over Wi-Fi, må du først be operatøren om å konfigurere denne tjenesten. Deretter slår du på Wi-Fi-anrop igjen fra Innstillinger. (Feilkode: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Registrer deg hos operatøren din"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi-anrop"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB for MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Koblet til et USB-tilbehør"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Trykk for å få flere alternativ."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Lydtilbehøret støttes ikke"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Trykk for mer informasjon"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-feilsøking tilkoblet"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Trykk for å slå av feilsøking via USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Velg for å deaktivere USB-debugging."</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 3d255fb..753acef 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Wi-Fi मार्फत कलहरू गर्न र सन्देशहरू पठाउन सबभन्दा पहिला आफ्नो सेवा प्रदायकलाई यो सेवा सेट गर्न भन्नुहोस्। त्यसपछि सेटिङहरूबाट Wi-Fi कलिङलाई सक्रिय पार्नुहोस्। (त्रुटिसम्बन्धी कोड: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"तपाईँको वाहकसँग दर्ता गर्नुहोस्"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi कलिङ"</item>
@@ -1191,8 +1189,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI को लागि USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB सहायकमा जोडिएको छ"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"थप विकल्पहरूका लागि ट्याप गर्नुहोस्।"</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"अडियोको सहायक सामग्रीलाई समर्थन छैन"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"थप जानकारीका लागि ट्याप गर्नुहोस्"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB डिबग गर्ने जडित छ"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"USB डिबगिङलाई असक्षम गर्न ट्याप गर्नुहोस्।"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB डिबगिङलाई असक्षम पार्न ट्याप गर्नुहोस्।"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index a8ae6f0..3d1c63d 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Als je wilt bellen en berichten wilt verzenden via wifi, moet je eerst je provider vragen deze service in te stellen. Schakel bellen via wifi vervolgens opnieuw in via Instellingen. (Foutcode: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Registreren bij je provider"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Bellen via wifi van %s"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB voor MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Aangesloten op een USB-accessoire"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Tik voor meer opties."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Audioaccessoire niet ondersteund"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tik voor meer informatie"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-foutopsporing verbonden"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Tik om USB-foutopsporing uit te schakelen."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecteer deze optie om USB-foutopsporing uit te schakelen."</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index a26d438..82f08e0 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Wi-Fi ਤੋਂ ਕਾਲਾਂ ਕਰਨ ਅਤੇ ਸੁਨੇਹੇ ਭੇਜਣ ਦੇ ਲਈ, ਸਭ ਤੋਂ ਪਹਿਲਾਂ ਆਪਣੇ ਕੈਰੀਅਰ ਨੂੰ ਇਸ ਸੇਵਾ ਦੀ ਸਥਾਪਨਾ ਕਰਨ ਲਈ ਕਹੋ। ਫਿਰ ਸੈਟਿੰਗਾਂ ਵਿੱਚੋਂ Wi-Fi ਕਾਲਿੰਗ ਨੂੰ ਦੁਬਾਰਾ ਚਾਲੂ ਕਰੋ। (ਗੜਬੜੀ ਕੋਡ: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"ਆਪਣੇ ਕੈਰੀਅਰ ਨਾਲ ਰਜਿਸਟਰ ਕਰੋ"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi ਕਾਲਿੰਗ"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI ਲਈ USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"ਇੱਕ USB ਐਕਸੈਸਰੀ ਨਾਲ ਕਨੈਕਟ ਕੀਤਾ"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"ਹੋਰ ਵਿਕਲਪਾਂ ਲਈ ਟੈਪ ਕਰੋ।"</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"ਔਡੀਓ ਉਪਸਾਧਨ ਸਮਰਥਿਤ ਨਹੀਂ ਹੈ"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"ਹੋਰ ਜਾਣਕਾਰੀ ਲਈ ਟੈਪ ਕਰੋ"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB ਡੀਬਗਿੰਗ ਕਨੈਕਟ ਕੀਤੀ"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"USB ਡੀਬੱਗਿੰਗ ਨੂੰ ਅਯੋਗ ਬਣਾਉਣ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB ਡੀਬੱਗਿੰਗ ਅਯੋਗ ਬਣਾਉਣ ਲਈ ਚੁਣੋ।"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 6de48e1..933b8bf 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -119,9 +119,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Aby dzwonić i wysyłać wiadomości przez Wi-Fi, poproś swojego operatora o skonfigurowanie tej usługi. Potem ponownie włącz połączenia przez Wi-Fi w Ustawieniach. (Kod błędu: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Zarejestruj u operatora"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Połączenia przez Wi-Fi (%s)"</item>
@@ -1229,8 +1227,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB w trybie MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Podłączono akcesorium USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Kliknij, by wyświetlić więcej opcji."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Nieobsługiwane akcesorium audio"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Kliknij, aby uzyskać więcej informacji"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Podłączono moduł debugowania USB"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Kliknij, by wyłączyć debugowanie USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Wybierz, aby wyłączyć debugowanie USB."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index d880233..1655bcf 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Para fazer chamadas e enviar mensagens por Wi-Fi, primeiro peça à sua operadora para configurar esse serviço. Depois, ative novamente a chamada no Wi-Fi nas configurações. Código de erro: <xliff:g id="CODE">%1$s</xliff:g>"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Faça registro na sua operadora"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s chamada Wi-Fi"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Conectado a um acessório USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Toque para ver mais opções."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Acessório de áudio não compatível"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Toque para mais informações"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Depuração USB conectada"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Toque para desativar a depuração USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecione para desativar a depuração USB."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 3bc922d..abe5ecb 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Para fazer chamadas e enviar mensagens por Wi-Fi, comece por pedir ao seu operador para configurar este serviço. De seguida, nas Definições, ative novamente as Chamadas Wi-Fi. (Código de erro: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Registar-se junto do seu operador"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Chamadas por Wi-Fi da %s"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Ligado a um acessório USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Toque para obter mais opções."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Acessório de áudio não suportado"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Toque para obter mais informações"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Depuração USB ligada"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Toque para desativar a depuração USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecione para desativar a depuração por USB."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index d880233..1655bcf 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Para fazer chamadas e enviar mensagens por Wi-Fi, primeiro peça à sua operadora para configurar esse serviço. Depois, ative novamente a chamada no Wi-Fi nas configurações. Código de erro: <xliff:g id="CODE">%1$s</xliff:g>"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Faça registro na sua operadora"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s chamada Wi-Fi"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Conectado a um acessório USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Toque para ver mais opções."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Acessório de áudio não compatível"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Toque para mais informações"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Depuração USB conectada"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Toque para desativar a depuração USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selecione para desativar a depuração USB."</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 7cfc00c..4c40f1c 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -118,9 +118,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Pentru a efectua apeluri și a trimite mesaje prin Wi-Fi, mai întâi solicitați configurarea acestui serviciu la operator. Apoi, activați din nou apelarea prin Wi-Fi din Setări. (Cod de eroare: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Înregistrați-vă la operatorul dvs."</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Apelare prin Wi-Fi %s"</item>
@@ -1207,8 +1205,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"Conexiune USB pentru MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Conectat la un accesoriu USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Atingeți pentru mai multe opțiuni."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Accesoriul audio nu este acceptat"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Atingeți pentru mai multe informații"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Depanarea USB este conectată"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Atingeți ca să dezactivați remedierea erorilor prin USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Selectați pentru a dezactiva depanarea USB."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 203c8b0..f6f1934 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -119,9 +119,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Чтобы совершать звонки и отправлять сообщения по Wi-Fi, необходимо подключить эту услугу через оператора связи. После этого вы сможете выбрать этот параметр в настройках. Код ошибки: <xliff:g id="CODE">%1$s</xliff:g>."</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Укажите оператора и зарегистрируйтесь"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Звонки по Wi-Fi (%s)"</item>
@@ -1229,8 +1227,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI через USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB-устройство подключено"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Нажмите, чтобы показать дополнительные параметры."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Аудиоустройство не поддерживается"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Нажмите, чтобы получить дополнительную информацию"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Отладка по USB разрешена"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Нажмите, чтобы отключить отладку по USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Нажмите, чтобы отключить отладку по USB."</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index b8e7be0..d588ad9 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -118,7 +118,7 @@
     <item msgid="3910386316304772394">"Wi-Fi හරහා ඇමතුම් සිදු කිරීමට සහ පණිවිඩ යැවීමට, පළමුව මෙම සේවාව පිහිටුවන ලෙස ඔබේ වාහකයෙන් ඉල්ලන්න. අනතුරුව සැකසීම් වෙතින් Wi-Fi ඇමතුම නැවත ක්‍රියාත්මක කරන්න. (දෝෂ කේතය <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
   <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"ඔබගේ වාහකය සමඟ ලියාපදිංචි වන්න"</item>
+    <item msgid="7472393097168811593">"ඔබගේ වාහකය සමඟ ලියාපදිංචි වන්න (දෝෂ කේතය: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
@@ -1187,8 +1187,8 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI සඳහා USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB මෙවලමකට සම්බන්ධිතයි"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"තවත් විකල්ප සඳහා තට්ටු කරන්න."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"ශ්‍රව්‍ය ආයිත්තම සහාය නොදක්වයි"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"තවත් තොරතුරු සඳහා තට්ටු කරන්න"</string>
+    <string name="usb_unsupported_audio_accessory_title" msgid="3529881374464628084">"ප්‍රතිසම ශ්‍රව්‍ය උපාංගය අනාවරණය කර ගන්නා ලදී"</string>
+    <string name="usb_unsupported_audio_accessory_message" msgid="6309553946441565215">"මෙම දුරකථනය සමඟ සම්බන්ධිත උපාංගය නොගැළපෙයි. තව දැන ගැනීමට තට්ටු කරන්න."</string>
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB නිදොස්කරණය සම්බන්ධිතයි"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"USB නිදොස්කරණය අබල කිරීමට තට්ටු කරන්න."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB නිදොස්කරණය අබල කිරීමට තෝරන්න."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 0ecfcbb..b781ec9 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -119,9 +119,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Ak chcete volať a odosielať správy prostredníctvom siete Wi-Fi, kontaktujte najskôr svojho operátora v súvislosti s nastavením tejto služby. Potom opäť zapnite v Nastaveniach volanie cez Wi-Fi. (Kód chyby: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Registrujte sa so svojím operátorom"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Volanie siete Wi-Fi %s"</item>
@@ -1229,8 +1227,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB na pripojenie zariadenia MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Pripojené k periférnemu zariadeniu USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Klepnutím zobrazíte ďalšie možnosti."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Zvukové príslušenstvo nie je podporované"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Ďalšie informácie zobrazíte klepnutím"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Ladenie cez USB pripojené"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Klepnutím zakážete ladenie cez USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Vyberte, ak chcete zakázať ladenie cez USB."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 9330e4b..4833617 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -119,9 +119,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Če želite klicati ali pošiljati sporočila prek omrežja Wi-Fi, se najprej obrnite na operaterja, da nastavi to storitev. Nato v nastavitvah znova vklopite klicanje prek omrežja Wi-Fi. (Koda napake: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Registracija pri operaterju"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Klicanje prek Wi-Fi-ja (%s)"</item>
@@ -1229,8 +1227,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB za MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Priključen na dodatek USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Dotaknite se za več možnosti."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Zvočna oprema ni podprta"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Dotaknite se za več informacij"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Iskanje napak prek USB je povezano"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Dotaknite se za izklop odpravljanja napak prek USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Izberite, če želite onemogočiti iskanje in odpravljanje napak prek vrat USB."</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 78cd1e0..3c24971 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Për të bërë telefonata dhe për të dërguar mesazhe nëpërmjet Wi-Fi, në fillim kërkoji operatorit celular të konfigurojë këtë shërbim. Më pas aktivizo përsëri telefonatat me Wi-Fi nga \"Cilësimet\". (Kodi i gabimit: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Regjistrohu me operatorin tënd celular"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Telefonatat me Wi-Fi nga %s"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB për MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"U lidh me një ndihmës USB-je"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Trokit për më shumë opsione."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Aksesori i audios nuk mbështetet"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Trokit për më shumë informacion"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Korrigjuesi i USB-së i lidhur"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Trokit për të çaktivizuar korrigjimin e gabimeve të USB-së."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Përzgjidhe për të çaktivizuar korrigjimin e gabimeve të USB-së"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 37b177e..23a18a8 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -118,9 +118,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Да бисте упућивали позиве и слали поруке преко Wi-Fi-ја, прво затражите од мобилног оператера да вам омогући ову услугу. Затим у Подешавањима поново укључите Позивање преко Wi-Fi-ја. (кôд грешке: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Региструјте се код мобилног оператера"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Wi-Fi позивање преко оператера %s"</item>
@@ -1207,8 +1205,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB за MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Повезано са USB додатком"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Додирните за још опција."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Додатна опрема за аудио садржај није подржана"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Додирните за више информација"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Отклањање грешака са USB-а је омогућено"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Додирните да бисте онемогућили отклањање грешака са USB-а."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Изаберите да бисте онемогућили отклањања грешака са USB-а."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 4747834..392b27c 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Om du vill ringa samtal och skicka meddelanden via Wi-Fi ber du först operatören att konfigurera tjänsten. Därefter kan du aktivera Wi-Fi-samtal på nytt från Inställningar. (Felkod: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Registrera dig hos operatören"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi-samtal"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB för MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Ansluten till ett USB-tillbehör"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Tryck för fler alternativ."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Ljudtillbehöret stöds inte"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tryck för mer information"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB-felsökning ansluten"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Tryck om du vill inaktivera USB-felsökning."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Välj för att inaktivera USB-felsökning."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 9316b0a..012ed52 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Ili upige simu na kutuma ujumbe kupitia Wi-Fi, mwambie mtoa huduma wako aweke mipangilio ya huduma hii kwanza. Kisha uwashe tena kipengele cha kupiga simu kupitia Wi-Fi kwenye Mipangilio. (Msimbo wa hitilafu: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Jisajili na mtoa huduma wako"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
     <!-- String.format failed for translation -->
     <!-- no translation found for wfcSpnFormats:0 (6830082633573257149) -->
     <string name="wifi_calling_off_summary" msgid="8720659586041656098">"Imezimwa"</string>
@@ -1183,8 +1181,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB kwa ajili ya MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Imeunganishwa kwa kifuasi cha USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Gonga ili upate chaguo zaidi."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Kifuasi cha sauti hakiwezi kutumika"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Gonga ili upate maelezo zaidi"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Utatuaji wa USB umeunganishwa"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Gonga ili uzime utatuaji wa USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Chagua ili kulemaza utatuaji USB."</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 516d079..cc0b295 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"வைஃபை மூலம் அழைக்கவும் செய்திகளை அனுப்பவும், முதலில் தொலைத்தொடர்பு நிறுவனத்திடம் இந்தச் சேவையை அமைக்குமாறு கேட்கவும். பிறகு அமைப்புகளில் மீண்டும் வைஃபை அழைப்பை இயக்கவும். (பிழைக் குறியீடு <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"உங்கள் மொபைல் நிறுவனத்தில் பதிவுசெய்யவும்"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s வைஃபை அழைப்பு"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB, MIDIக்கு மட்டும்"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB துணைக்கருவியுடன் இணைக்கப்பட்டுள்ளது"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"மேலும் விருப்பங்களுக்கு, தட்டவும்."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"ஆடியோ துணைக்கருவி ஆதரிக்கப்படவில்லை"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"மேலும் தகவலுக்கு, தட்டவும்"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB பிழைதிருத்தம் இணைக்கப்பட்டது"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"USB பிழை திருத்தத்தை முடக்க, தட்டவும்."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB பிழைத்திருத்தத்தை முடக்க, தேர்ந்தெடுக்கவும்."</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 4fae01d..e79edcf 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Wi-Fiతో కాల్‌లను చేయడానికి మరియు సందేశాలను పంపించడానికి, మొదట ఈ సేవను సెటప్ చేయాల్సిందిగా మీ క్యారియర్‌‌కి చెప్పండి. ఆ తర్వాత సెట్టింగ్‌ల నుండి Wi-Fi కాలింగ్‌ని మళ్లీ ఆన్ చేయండి. (లోపం కోడ్: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"మీ క్యారియర్‌తో నమోదు చేయండి"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi కాలింగ్"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI కోసం USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB ఉపకరణానికి కనెక్ట్ చేయబడింది"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"మరిన్ని ఎంపికల కోసం నొక్కండి."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"ఆడియో ఉపకరణానికి మద్దతు లేదు"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"మరింత సమాచారం కోసం నొక్కండి"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB డీబగ్గింగ్ కనెక్ట్ చేయబడింది"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"USB డీబగ్గింగ్‌ను నిలిపివేయడానికి నొక్కండి."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"డీబగ్గింగ్‌ని నిలిపివేయడానికి ఎంచుకోండి."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index f5b4cd6..1226f58 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"หากต้องการโทรออกและส่งข้อความผ่าน Wi-Fi โปรดสอบถามผู้ให้บริการของคุณก่อนเพื่อตั้งค่าบริการนี้ แล้วเปิดการโทรผ่าน Wi-Fi อีกครั้งจากการตั้งค่า (รหัสข้อผิดพลาด: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"ลงทะเบียนกับผู้ให้บริการ"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"กำลังเรียก Wi-Fi ของ %s"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB สำหรับ MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"เชื่อมต่อกับอุปกรณ์เสริม USB แล้ว"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"แตะเพื่อดูตัวเลือกเพิ่มเติม"</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"ไม่รองรับอุปกรณ์เสริมสำหรับเสียง"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"แตะเพื่อดูข้อมูลเพิ่มเติม"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"เชื่อมต่อการแก้ไขข้อบกพร่องผ่าน USB แล้ว"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"แตะเพื่อปิดใช้การแก้ไขข้อบกพร่องของ USB"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"เลือกเพื่อปิดใช้งานการแก้ไขข้อบกพร่อง USB"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 43a8618..9890e347 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Upang makatawag at makapagpadala ng mga mensahe sa Wi-Fi, hilingin muna sa iyong carrier na i-set up ang serbisyong ito. Pagkatapos ay i-on muli ang pagtawag gamit ang Wi-Fi mula sa Mga Setting. (Error code: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Magparehistro sa iyong carrier"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Pagtawag sa Pamamagitan ng Wi-Fi ng %s"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB para sa MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Nakakonekta sa isang accessory ng USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"I-tap para sa higit pang mga opsyon."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Hindi sinusuportahan ang audio accessory"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"I-tap para sa higit pang impormasyon"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Konektado ang debugging ng USB"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"I-tap upang i-disable ang pag-debug ng USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Piliin upang i-disable ang debugging ng USB."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 918ce51..5e1de8c 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Kablosuz ağ üzerinden telefon etmek ve mesaj göndermek için öncelikle operatörünüzden bu hizmeti ayarlamasını isteyin. Sonra, Ayarlar\'dan Kablosuz çağrı özelliğini tekrar açın. (Hata kodu: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Operatörünüze kaydolun"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Kablosuz Çağrı"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"MIDI için USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB aksesuarına bağlandı"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Diğer seçenekler için dokunun."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Ses aksesuarı desteklenmiyor"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Daha fazla bilgi için dokunun"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB hata ayıklaması bağlandı"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"USB hata ayıklama özelliğini devre dışı bırakmak için dokunun."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB hata ayıklamasını devre dışı bırakmak için seçin."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index d0b40cd..6761f52 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -119,9 +119,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Щоб телефонувати або надсилати повідомлення через Wi-Fi, спершу попросіть свого оператора налаштувати цю послугу. Після цього знову ввімкніть дзвінки через Wi-Fi у налаштуваннях. (Код помилки: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Зареєструйтеся в оператора"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Дзвінок через Wi-Fi від оператора %s"</item>
@@ -1229,8 +1227,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB для режиму MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Під’єднано до аксесуара USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Торкніться, щоб переглянути більше опцій."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Аксесуар для аудіо не підтримується"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Торкніться, щоб дізнатися більше"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Налагодження USB завершено"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Торкніться, щоб вимкнути налагодження USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Виберіть, щоб вимкнути налагодження за USB"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 28875f3..94cf61e 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"‏Wi-Fi سے کالز کرنے اور پیغامات بھیجنے کے لیے، پہلے اپنے کیریئر سے اس سروس کو سیٹ اپ کرنے کے لیے کہیں۔ پھر ترتیبات سے دوبارہ Wi-Fi کالنگ آن کریں۔ (خراب کوڈ: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"اپنے کیریئر کے ساتھ رجسٹر کریں"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"‎%s"</item>
     <item msgid="4397097370387921767">"‏‎%s Wi-Fi کالنگ"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"‏MIDI کیلئے USB"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"‏ایک USB لوازم سے مربوط ہے"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"مزید اختیارات کیلئے تھپتھپائیں۔"</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"آڈیو لوازم تعاون یافتہ نہیں ہے"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"مزید معلومات کے لیے تھپتھپائيں"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"‏USB ڈیبگ کرنا مربوط ہو گیا"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"‏USB ڈیبگنگ کو غیر فعال کرنے کیلئے تھپتھپائیں۔"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"‏USB ڈیبگ کرنے کو غیر فعال کرنے کیلئے منتخب کریں۔"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 338ad3a..73b2ef5 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Wi-Fi orqali qo‘ng‘iroqlarni amalga oshirish va xabarlar bilan almashinish uchun uyali aloqa operatoringizdan ushbu xizmatni yoqib qo‘yishni so‘rashingiz lozim. Keyin sozlamalarda Wi-Fi qo‘ng‘irog‘i imkoniyatini yoqib olishingiz mumkin. (Xato kodi: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Mobil operatoringiz yordamida ro‘yxatdan o‘ting"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi qo‘ng‘iroqlar"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB orqali MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"USB jihozga ulangan"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Boshqa parametrlarini ko‘rish uchun bosing."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Audio aksessuar ishlamaydi"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Tafsilotlar uchun bosing"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"USB orqali nosozliklarni tuzatish"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Faolsizlantirish uchun bu yerga bosing."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"USB orqali nosozliklarni tuzatishni o‘chirib qo‘yish uchun bosing."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 1f9e794..80ec90d 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Để gọi điện và gửi tin nhắn qua Wi-Fi, trước tiên hãy yêu cầu nhà cung cấp dịch vụ của bạn thiết lập dịch vụ này. Sau đó, bật lại gọi qua Wi-Fi từ Cài đặt. (Mã lỗi: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Đăng ký với nhà cung cấp dịch vụ của bạn"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"Gọi điện qua Wi-Fi %s"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB cho MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Đã kết nối với phụ kiện USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Nhấn để biết thêm tùy chọn."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Phụ kiện âm thanh không được hỗ trợ"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Nhấn để biết thêm thông tin"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Đã kết nối gỡ lỗi USB"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Nhấn để vô hiệu hóa gỡ lỗi USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Chọn để vô hiệu hóa gỡ lỗi USB."</string>
diff --git a/core/res/res/values-watch/colors_device_defaults.xml b/core/res/res/values-watch/colors_device_defaults.xml
index 15786b4..654e92c 100644
--- a/core/res/res/values-watch/colors_device_defaults.xml
+++ b/core/res/res/values-watch/colors_device_defaults.xml
@@ -17,8 +17,87 @@
 <!-- Colors specific to DeviceDefault themes. These are mostly pass-throughs to enable
      overlaying new theme colors. -->
 <resources>
+
+    <!--
+       primary_device_default_dark
+         > from values/colors_material/primary_material_dark
+         > from values/colors_material/material_grey_900
+         = #ff212121
+         ! replaced with custom color #33ffffff
+    -->
+    <color name="primary_device_default_dark">#33ffffff</color>
+    <!--
+       primary_dark_device_default_dark
+         > from values/colors_material/primary_dark_material_dark
+         = @color/black
+    -->
+    <color name="primary_dark_device_default_dark">@color/black</color>
+    <!--
+       accent_device_default_dark
+         > from values/colors_material/accent_material_dark
+         > from values/colors_material/material_deep_teal_200
+         = #ff80cbc4
+         ! replaced with custom color #5E97F6
+    -->
+    <color name="accent_device_default_dark">#5E97F6</color>
+    <!--
+       background_device_default_dark
+         > from values/colors_material/background_material_dark
+         > from values/colors_material/material_grey_850
+         = #ff303030
+         ! replaced with custom color #232E33
+    -->
+    <color name="background_device_default_dark">#232E33</color>
+    <!--
+       background_floating_device_default_dark
+         > from values/colors_material/background_floating_material_dark
+         > from values/colors_material/material_grey_800
+         = #ff424242
+         ! replaced with custom color #3E5059
+    -->
+    <color name="background_floating_device_default_dark">#3E5059</color>
+
+    <!--
+       background_cache_hint_selector_device_default
+         - note that this is based off of colors/background_cache_hint_selector_device_default
+           xml drawable
+         - uses ?attr/colorBackground and transparency to draw
+         - no color customization required here
+    -->
+
+    <!--
+       button_normal_device_default_dark
+         - uses ?attr/disabledAlpha and button_material_dark to draw
+         - cloned to watch_btn_default.xml drawable
+           (btn_default_material_dark & button_material_dark - see
+           values-watch/colors_material.xml)
+    -->
     <color name="button_normal_device_default_dark">@color/btn_default_material_dark</color>
+
     <!-- Use the same value as for accent_device_default_dark but start with #99,
          i.e. 60% opacity -->
     <color name="accent_device_default_dark_60_percent_opacity">#995E97f6</color>
+
+    <!--
+       foreground_device_default_dark
+         - introduced to avoid coupling to foreground_material_dark
+         - colorForeground typically falls through Theme.DeviceDefault to Theme.Material
+    -->
+    <color name="foreground_device_default_dark">@color/white</color>
+
+    <!--
+       error_color_device_default_dark
+         - introduced to avoid coupling to error_color_material (also #F4511E)
+         - colorError typically falls through Theme.DeviceDefault to Theme.Material
+    -->
+    <color name="error_color_device_default_dark">#F4511E</color>
+
+    <!-- deprecated for Wear
+         these overrides exist only for compatibility with existing
+         WTS theme test heuristics, based on the previous modifications
+         to the material theme, they should not be used for customization
+         as they are not exposed via publicly accessible attributes -->
+    <color name="accent_device_default_700">#5385DB</color>
+    <color name="accent_device_default_light">#75A4F5</color>
+    <color name="accent_device_default_50">#93B7F5</color>
 </resources>
diff --git a/core/res/res/values-watch/colors_material.xml b/core/res/res/values-watch/colors_material.xml
index 72f589b..b19820c 100644
--- a/core/res/res/values-watch/colors_material.xml
+++ b/core/res/res/values-watch/colors_material.xml
@@ -14,15 +14,8 @@
      limitations under the License.
 -->
 <resources>
-    <color name="background_material_dark">#232E33</color>
-    <color name="background_floating_material_dark">#3E5059</color>
 
-    <color name="accent_material_700">#5385DB</color>
-    <color name="accent_material_light">#75A4F5</color>
-    <color name="accent_material_dark">#5E97F6</color>
-    <color name="accent_material_50">#93B7F5</color>
+  <!-- referenced in colors/watch_btn_default.xml selector -->
+  <color name="button_material_dark">#ff919699</color>
 
-    <color name="primary_material_dark">#33ffffff</color>
-
-    <color name="button_material_dark">#ff919699</color>
 </resources>
diff --git a/core/res/res/values-watch/dimens_device_defaults.xml b/core/res/res/values-watch/dimens_device_defaults.xml
new file mode 100644
index 0000000..ac0fcb8
--- /dev/null
+++ b/core/res/res/values-watch/dimens_device_defaults.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+<resources>
+  <!-- Alpha transparency for wigets that set enablement/disablement programmatically
+       transparency is applied in the disabled state -->
+    <dimen name="disabled_alpha_device_default">0.5</dimen>
+  <!-- Alpha transparency applied to elements which are considered primary (e.g. primary text) -->
+    <dimen name="primary_content_alpha_device_default">1.0</dimen>
+  <!-- Alpha transparency applied to elements which are considered secondary (e.g. secondary text) -->
+    <dimen name="secondary_content_alpha_device_default">0.8</dimen>
+</resources>
diff --git a/core/res/res/values-watch/themes_device_defaults.xml b/core/res/res/values-watch/themes_device_defaults.xml
index 4d210f6..f6752c2 100644
--- a/core/res/res/values-watch/themes_device_defaults.xml
+++ b/core/res/res/values-watch/themes_device_defaults.xml
@@ -37,11 +37,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar -->
@@ -49,11 +54,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar.  This theme
@@ -62,11 +72,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
@@ -77,11 +92,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
@@ -91,11 +111,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- Theme used for the intent picker activity. -->
@@ -134,11 +159,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- DeviceDefault theme for a window that should look like the Settings app.  -->
@@ -156,11 +186,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <style name="Theme.DeviceDefault.Settings.CompactMenu" parent="Theme.Material.CompactMenu">
@@ -168,11 +203,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
@@ -182,11 +222,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
@@ -195,11 +240,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
@@ -209,11 +259,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
    <!-- DeviceDefault theme for a window that will be displayed either full-screen on smaller
@@ -222,11 +277,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- DeviceDefault theme for a window without an action bar that will be displayed either
@@ -236,11 +296,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- DeviceDefault theme for a presentation window on a secondary display. -->
@@ -248,11 +313,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- DeviceDefault theme for panel windows. This removes all extraneous window
@@ -262,11 +332,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -275,11 +350,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -288,11 +368,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <!-- DeviceDefault style for input methods, which is used by the
@@ -301,11 +386,16 @@
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
@@ -314,22 +404,32 @@
         <!-- Color palette Dialog -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <style name="Theme.DeviceDefault.SearchBar" parent="Theme.Material.SearchBar">
         <!-- Color palette Dark -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 
     <style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
@@ -337,10 +437,15 @@
         <!-- Color palette Dialog -->
         <item name="colorPrimary">@color/primary_device_default_dark</item>
         <item name="colorPrimaryDark">@color/primary_dark_device_default_dark</item>
+        <item name="colorForeground">@color/foreground_device_default_dark</item>
         <item name="colorAccent">@color/accent_device_default_dark</item>
         <item name="colorBackground">?attr/colorBackgroundFloating</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>
+        <item name="colorError">@color/error_color_device_default_dark</item>
+        <item name="disabledAlpha">@dimen/disabled_alpha_device_default</item>
+        <item name="primaryContentAlpha">@dimen/primary_content_alpha_device_default</item>
+        <item name="secondaryContentAlpha">@dimen/secondary_content_alpha_device_default</item>
     </style>
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index ebc1d45..f93ea0a 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"要通过 WLAN 打电话和发信息,请先让您的运营商开通此服务,然后再到“设置”中重新开启 WLAN 通话功能(错误代码:<xliff:g id="CODE">%1$s</xliff:g>)。"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"向您的运营商注册"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s WLAN 通话功能"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"正在通过 USB 连接到 MIDI 接口"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"已连接到USB配件"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"点按即可查看更多选项。"</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"音频配件不受支持"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"点按即可了解详情"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"已连接到 USB 调试"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"点按即可停用 USB 调试功能。"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"选择即可停用 USB 调试功能。"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 0841177..05d47a3 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"如要透過 Wi-Fi 撥打電話和傳送訊息,請先向流動網絡供應商要求設定此服務,然後再次在「設定」中開啟「Wi-Fi 通話」。(錯誤代碼:<xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"向您的流動網絡供應商註冊"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi 通話"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"已連接到一個 USB 配件"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"輕按即可查看更多選項。"</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"不支援的音訊配件"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"輕按即可瞭解詳情"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"已連接 USB 偵錯工具"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"輕按即可停用 USB 偵錯功能。"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"選取即可停用 USB 偵錯。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 8790fe6..d36903a 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"如要透過 Wi-Fi 網路撥打電話及傳送訊息,請先要求電信業者為你設定這項服務,然後再次前往「設定」頁面啟用 Wi-Fi 通話功能。(錯誤代碼:<xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"向你的電信業者註冊"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s Wi-Fi 通話"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"USB MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"已連接 USB 配件"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"輕觸即可查看更多選項。"</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"不支援的音訊配件"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"輕觸即可瞭解詳情"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"已連接 USB 偵錯工具"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"輕觸即可停用 USB 偵錯功能。"</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"選取這個選項以停用 USB 偵錯功能。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index cd66d45..7a00cee 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -117,9 +117,7 @@
   <string-array name="wfcOperatorErrorAlertMessages">
     <item msgid="3910386316304772394">"Ukuze wenze amakholi uphinde uthumele imilayezo nge-Wi-Fi, qala ucele inkampani yakho yenethiwekhi ukuthi isethe le sevisi. Bese uvula ukushaya kwe-Wi-Fi futhi kusukela kuzilungiselelo (Ikhodi yephutha: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
   </string-array>
-  <string-array name="wfcOperatorErrorNotificationMessages">
-    <item msgid="6177300162212449033">"Bhalisa ngenkampani yakho yenethiwekhi"</item>
-  </string-array>
+    <!-- no translation found for wfcOperatorErrorNotificationMessages:0 (7472393097168811593) -->
   <string-array name="wfcSpnFormats">
     <item msgid="6830082633573257149">"%s"</item>
     <item msgid="4397097370387921767">"%s ukushaya kwe-Wi-Fi"</item>
@@ -1185,8 +1183,10 @@
     <string name="usb_midi_notification_title" msgid="4850904915889144654">"I-USB ye-MIDI"</string>
     <string name="usb_accessory_notification_title" msgid="7848236974087653666">"Ixhunywe ku-accessory ye-USB"</string>
     <string name="usb_notification_message" msgid="3370903770828407960">"Thepha ngezinketho eziningi."</string>
-    <string name="usb_unsupported_audio_accessory_title" msgid="2256529893240208458">"Insiza yomsindo ayisekelwa"</string>
-    <string name="usb_unsupported_audio_accessory_message" msgid="7811865061127547035">"Thepha ngolwazi olungeziwe"</string>
+    <!-- no translation found for usb_unsupported_audio_accessory_title (3529881374464628084) -->
+    <skip />
+    <!-- no translation found for usb_unsupported_audio_accessory_message (6309553946441565215) -->
+    <skip />
     <string name="adb_active_notification_title" msgid="6729044778949189918">"Ukulungisa iphutha le-USB kuxhunyiwe"</string>
     <string name="adb_active_notification_message" msgid="4948470599328424059">"Thepha ukuze ukhubaze ukususa isiphazamisi se-USB."</string>
     <string name="adb_active_notification_message" product="tv" msgid="8470296818270110396">"Khetha ukuvimbela ukulungisa iphutha le-USB."</string>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index c2e9759..07a9a33 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -561,19 +561,19 @@
          to be set for all windows of this activity -->
     <attr name="showForAllUsers" format="boolean" />
 
-    <!-- Specifies whether an {@link Activity} should be shown on top of the the lock screen
+    <!-- Specifies whether an {@link android.app.Activity} should be shown on top of the the lock screen
          whenever the lockscreen is up and the activity is resumed. Normally an activity will be
          transitioned to the stopped state if it is started while the lockscreen is up, but with
          this flag set the activity will remain in the resumed state visible on-top of the lock
          screen.
 
-         <p>This should be used instead of {@link android.view.LayoutParams#FLAG_SHOW_WHEN_LOCKED}
+         <p>This should be used instead of {@link android.view.WindowManager.LayoutParams#FLAG_SHOW_WHEN_LOCKED}
          flag set for Windows. When using the Window flag during activity startup, there may not be
          time to add it before the system stops your activity for being behind the lock-screen.
          This leads to a double life-cycle as it is then restarted.</p> -->
     <attr name="showWhenLocked" format="boolean" />
 
-    <!-- Specifies whether the screen should be turned on when the {@link Activity} is resumed.
+    <!-- Specifies whether the screen should be turned on when the {@link android.app.Activity} is resumed.
          Normally an activity will be transitioned to the stopped state if it is started while the
          screen if off, but with this flag set the activity will cause the screen to turn on if the
          activity will be visible and resumed due to the screen coming on. The screen will not be
@@ -581,10 +581,10 @@
          normally used in conjunction with the {@link android.R.attr#showWhenLocked} flag to make
          sure the activity is visible after the screen is turned on when the lockscreen is up. In
          addition, if this flag is set and the activity calls
-         {@link KeyguardManager#requestDismissKeyguard(Activity, KeyguardManager.KeyguardDismissCallback)}
+         {@link android.app.KeyguardManager#requestDismissKeyguard}
          the screen will turn on.
 
-         <p>This should be used instead of {@link android.view.LayoutParams.FLAG_TURN_SCREEN_ON}
+         <p>This should be used instead of {@link android.view.WindowManager.LayoutParams#FLAG_TURN_SCREEN_ON}
          flag set for Windows. When using the Window flag during activity startup, there may not be
          time to add it before the system stops your activity because the screen has not yet turned
          on. This leads to a double life-cycle as it is then restarted.</p> -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4038010..4b64e3f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -525,6 +525,9 @@
     <!-- Integers specifying the max packet Tx/Rx rates for full scan -->
     <integer translatable="false" name="config_wifi_framework_max_tx_rate_for_full_scan">8</integer>
     <integer translatable="false" name="config_wifi_framework_max_rx_rate_for_full_scan">16</integer>
+    <!-- Integers specifying the min packet Tx/Rx rates in packets per second for staying on the same network -->
+    <integer translatable="false" name="config_wifi_framework_min_tx_rate_for_staying_on_network">16</integer>
+    <integer translatable="false" name="config_wifi_framework_min_rx_rate_for_staying_on_network">16</integer>
     <!-- Integer parameters of the wifi to cellular handover feature
          wifi should not stick to bad networks -->
     <integer translatable="false" name="config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz">-82</integer>
@@ -1388,6 +1391,7 @@
     <!-- The package name of the default network recommendation app.
          A network recommendation provider must:
              * Be granted the SCORE_NETWORKS permission.
+             * Be granted the ACCESS_COARSE_LOCATION permission.
              * Include a Service for the android.net.scoring.RECOMMEND_NETWORKS action
                protected by the BIND_NETWORK_RECOMMENDATION_SERVICE permission.
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c4ef04b..0ff24b7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -153,6 +153,10 @@
     <string name="NetworkPreferenceSwitchTitle">Can\u2019t reach network</string>
     <!-- Displayed to tell the user that they should switch their network preference. -->
     <string name="NetworkPreferenceSwitchSummary">To improve reception, try changing the type selected at Settings &gt; Network &amp; Internet &gt; Mobile networks &gt; Preferred network type."</string>
+    <!-- Displayed to tell the user that emergency calls might not be available. -->
+    <string name="EmergencyCallWarningTitle">Wi\u2011Fi calling is active</string>
+    <!-- Displayed to tell the user that emergency calls might not be available. -->
+    <string name="EmergencyCallWarningSummary">Emergency calls require a mobile network.</string>
 
     <!-- Telephony notification channel name for a channel containing network alert notifications. -->
     <string name="notification_channel_network_alert">Alerts</string>
@@ -160,8 +164,8 @@
     <string name="notification_channel_call_forward">Call forwarding</string>
     <!-- Telephony notification channel name for a channel containing emergency callback mode notifications. -->
     <string name="notification_channel_emergency_callback">Emergency callback mode</string>
-    <!-- Telephony notification channel name for a channel containing mobile data alert notifications. -->
-    <string name="notification_channel_mobile_data_alert">Mobile data alerts</string>
+    <!-- Telephony notification channel name for a channel containing mobile data status notifications. -->
+    <string name="notification_channel_mobile_data_status">Mobile data status</string>
     <!-- Telephony notification channel name for a channel containing sms notifications. -->
     <string name="notification_channel_sms">SMS messages</string>
     <!-- Telephony notification channel name for a channel containing voice mail notifications. -->
@@ -3471,6 +3475,13 @@
     <string name="tethered_notification_title">Tethering or hotspot active</string>
     <string name="tethered_notification_message">Tap to set up.</string>
 
+    <!-- Strings for tether disabling notification -->
+    <!-- This notification is shown when tethering has been disabled on a user's device.
+    The device is managed by the user's employer. Tethering can't be turned on unless the
+    IT administrator allows it. The noun "admin" is another reference for "IT administrator." -->
+    <string name="disable_tether_notification_title">Tethering is disabled</string>
+    <string name="disable_tether_notification_message">Contact your admin for details</string>
+
     <!--  Strings for possible PreferenceActivity Back/Next buttons -->
     <string name="back_button_label">Back</string>
     <string name="next_button_label">Next</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 196195f..7261377 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -377,6 +377,8 @@
   <java-symbol type="string"  name="config_wifi_framework_sap_2G_channel_list" />
   <java-symbol type="integer" name="config_wifi_framework_max_tx_rate_for_full_scan" />
   <java-symbol type="integer" name="config_wifi_framework_max_rx_rate_for_full_scan" />
+  <java-symbol type="integer" name="config_wifi_framework_min_tx_rate_for_staying_on_network" />
+  <java-symbol type="integer" name="config_wifi_framework_min_rx_rate_for_staying_on_network" />
 
   <java-symbol type="bool" name="config_wifi_framework_cellular_handover_enable_user_triggered_adjustment" />
   <java-symbol type="integer" name="config_wifi_framework_associated_full_scan_tx_packet_threshold" />
@@ -530,6 +532,8 @@
   <java-symbol type="string" name="PwdMmi" />
   <java-symbol type="string" name="NetworkPreferenceSwitchSummary" />
   <java-symbol type="string" name="NetworkPreferenceSwitchTitle" />
+  <java-symbol type="string" name="EmergencyCallWarningTitle" />
+  <java-symbol type="string" name="EmergencyCallWarningSummary" />
   <java-symbol type="string" name="RestrictedOnAllVoiceTitle" />
   <java-symbol type="string" name="RestrictedOnDataTitle" />
   <java-symbol type="string" name="RestrictedOnEmergencyTitle" />
@@ -538,7 +542,7 @@
   <java-symbol type="string" name="notification_channel_network_alert" />
   <java-symbol type="string" name="notification_channel_call_forward" />
   <java-symbol type="string" name="notification_channel_emergency_callback" />
-  <java-symbol type="string" name="notification_channel_mobile_data_alert" />
+  <java-symbol type="string" name="notification_channel_mobile_data_status" />
   <java-symbol type="string" name="notification_channel_sms" />
   <java-symbol type="string" name="notification_channel_voice_mail" />
   <java-symbol type="string" name="notification_channel_wfc" />
@@ -1954,6 +1958,8 @@
   <java-symbol type="string" name="smv_process" />
   <java-symbol type="string" name="tethered_notification_message" />
   <java-symbol type="string" name="tethered_notification_title" />
+  <java-symbol type="string" name="disable_tether_notification_message" />
+  <java-symbol type="string" name="disable_tether_notification_title" />
   <java-symbol type="string" name="adb_debugging_notification_channel_tv" />
   <java-symbol type="string" name="usb_accessory_notification_title" />
   <java-symbol type="string" name="usb_mtp_notification_title" />
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java
index ee40c48..29b6fd0 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/functional/RadioTunerTest.java
@@ -28,7 +28,9 @@
 
 import java.lang.reflect.Constructor;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.junit.After;
 import org.junit.Before;
@@ -291,6 +293,7 @@
         assertEquals(RadioManager.STATUS_OK, ret);
         assertNotNull(info[0]);
         assertEquals(channel, info[0].getChannel());
+        Log.d(TAG, "PI: " + info[0].toString());
     }
 
     @Test
@@ -344,7 +347,9 @@
         openTuner();
 
         try {
-            List<RadioManager.ProgramInfo> list = mRadioTuner.getProgramList(null);
+            Map<String, String> filter = new HashMap<>();
+            filter.put("com.google.dummy", "dummy");
+            List<RadioManager.ProgramInfo> list = mRadioTuner.getProgramList(filter);
             assertNotNull(list);
         } catch (IllegalStateException e) {
             // the list may or may not be ready at this point
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index e127896..ab9912a 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -978,6 +978,13 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.view.ViewTransientState" android:label="View Transient State">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="android.view.RemoteViewsActivity" android:label="RemoteViewsActicity">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
diff --git a/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml b/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml
index 4b01736..2c430e0 100644
--- a/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml
+++ b/core/tests/coretests/apks/install_complete_package_info/AndroidManifest.xml
@@ -40,15 +40,31 @@
 
     <application
         android:hasCode="true">
+        <meta-data android:name="key1" android:value="value1" />
+        <meta-data android:name="key2" android:value="this_is_app" />
+
         <activity
             android:name="com.android.frameworks.coretests.TestActivity">
+            <meta-data android:name="key1" android:value="value1" />
+            <meta-data android:name="key2" android:value="this_is_activity" />
         </activity>
         <provider
             android:name="com.android.frameworks.coretests.TestProvider"
-            android:authorities="com.android.frameworks.coretests.testprovider" />
+            android:authorities="com.android.frameworks.coretests.testprovider" >
+            <meta-data android:name="key1" android:value="value1" />
+            <meta-data android:name="key2" android:value="this_is_provider" />
+        </provider>
+
         <receiver
-            android:name="com.android.frameworks.coretests.TestReceiver" />
+            android:name="com.android.frameworks.coretests.TestReceiver" >
+            <meta-data android:name="key1" android:value="value1" />
+            <meta-data android:name="key2" android:value="this_is_receiver" />
+        </receiver>
+
         <service
-            android:name="com.android.frameworks.coretests.TestService" />
+            android:name="com.android.frameworks.coretests.TestService" >
+            <meta-data android:name="key1" android:value="value1" />
+            <meta-data android:name="key2" android:value="this_is_service" />
+        </service>
     </application>
 </manifest>
diff --git a/core/tests/coretests/res/layout/view_transient_state.xml b/core/tests/coretests/res/layout/view_transient_state.xml
new file mode 100644
index 0000000..8873015
--- /dev/null
+++ b/core/tests/coretests/res/layout/view_transient_state.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2017, 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.
+*/
+-->
+
+<!-- Demonstrates view transient state, See corresponding Java code. -->
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <FrameLayout
+        android:id="@+id/p1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+        <FrameLayout
+            android:id="@+id/p2"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content">
+            <TextView
+                android:id="@+id/p3"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content">
+            </TextView>
+
+        </FrameLayout>
+
+    </FrameLayout>
+
+</FrameLayout>
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java b/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java
new file mode 100644
index 0000000..00be822
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/PackageParserCacheHelperTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 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.content.pm;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.pm.PackageParserCacheHelper.ReadHelper;
+import android.content.pm.PackageParserCacheHelper.WriteHelper;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PackageParserCacheHelperTest {
+    @Test
+    public void testParcelUnparcel() throws Exception {
+        final Bundle source = new Bundle();
+        source.putInt("i1", 123);
+        source.putString("s1", "abcdef");
+        source.putString("s2", "xyz");
+        source.putString("s3", null);
+
+        final Bundle nest = new Bundle();
+        nest.putString("s1", "xyz");
+        source.putBundle("b1", nest);
+
+        final Parcel p = Parcel.obtain();
+        final WriteHelper writeHelper = new WriteHelper(p);
+
+        source.writeToParcel(p, 0);
+        writeHelper.finishAndUninstall();
+
+        p.setDataPosition(0);
+
+        final ReadHelper readHelper = new ReadHelper(p);
+        readHelper.startAndInstall();
+
+        final Bundle dest = new Bundle();
+        dest.readFromParcel(p);
+
+        dest.size(); // Unparcel so that toString() returns the content.
+
+        assertEquals(source.get("i1"), dest.get("i1"));
+        assertEquals(source.get("s1"), dest.get("s1"));
+        assertEquals(source.get("s2"), dest.get("s2"));
+        assertEquals(source.get("s3"), dest.get("s3"));
+        assertEquals(source.getBundle("b1").get("s1"), dest.getBundle("b1").get("s1"));
+        assertEquals(source.keySet().size(), dest.keySet().size());
+    }
+}
diff --git a/core/tests/coretests/src/android/content/pm/PackageParserTest.java b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
index 73c153f..53f4f3a 100644
--- a/core/tests/coretests/src/android/content/pm/PackageParserTest.java
+++ b/core/tests/coretests/src/android/content/pm/PackageParserTest.java
@@ -26,7 +26,9 @@
 import android.content.pm.PackageParser.Package;
 import android.content.pm.PackageParser.Permission;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.FileUtils;
+import android.os.SystemProperties;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.test.suitebuilder.annotation.SmallTest;
@@ -39,6 +41,7 @@
 import java.io.File;
 import java.io.InputStream;
 import java.util.Arrays;
+import java.util.function.Function;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -271,22 +274,27 @@
         assertEquals(0x0083, finalConfigChanges); // Should be 10000011.
     }
 
+    Package parsePackage(String apkFileName, int apkResourceId) throws Exception {
+        return parsePackage(apkFileName, apkResourceId, p -> p);
+    }
+
     /**
      * Attempts to parse a package.
      *
      * APKs are put into coretests/apks/packageparser_*.
      *
-     * @param apkName temporary file name to store apk extracted from resources
+     * @param apkFileName temporary file name to store apk extracted from resources
      * @param apkResourceId identifier of the apk as a resource
      */
-    Package parsePackage(String apkFileName, int apkResourceId) throws Exception {
+    Package parsePackage(String apkFileName, int apkResourceId,
+            Function<Package, Package> converter) throws Exception {
         // Copy the resource to a file.
         Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
         File outFile = new File(context.getFilesDir(), apkFileName);
         try {
             InputStream is = context.getResources().openRawResource(apkResourceId);
             assertTrue(FileUtils.copyToFile(is, outFile));
-            return new PackageParser().parsePackage(outFile, 0 /* flags */);
+            return converter.apply(new PackageParser().parsePackage(outFile, 0 /* flags */));
         } finally {
             outFile.delete();
         }
@@ -331,10 +339,39 @@
         assertEquals(protectionLevel, permission.info.protectionLevel);
     }
 
+    private void assertMetadata(Bundle b, String... keysAndValues) {
+        assertTrue("Odd number of elements in keysAndValues", (keysAndValues.length % 2) == 0);
+
+        assertNotNull(b);
+        assertEquals(keysAndValues.length / 2, b.size());
+
+        for (int i = 0; i < keysAndValues.length; i += 2) {
+            final String key = keysAndValues[i];
+            final String value = keysAndValues[i + 1];
+
+            assertEquals(value, b.getString(key));
+        }
+    }
+
+    // TODO Add a "_cached" test for testMultiPackageComponents() too, after fixing b/64295061.
+    // Package.writeToParcel can't handle circular package references.
+
     @Test
-    public void testPackageWithComponents() throws Exception {
+    public void testPackageWithComponents_no_cache() throws Exception {
+        checkPackageWithComponents(p -> p);
+    }
+
+    @Test
+    public void testPackageWithComponents_cached() throws Exception {
+        checkPackageWithComponents(p ->
+                PackageParser.fromCacheEntryStatic(PackageParser.toCacheEntryStatic(p)));
+    }
+
+    private void checkPackageWithComponents(
+            Function<Package, Package> converter) throws Exception {
         Package p = parsePackage(
-                "install_complete_package_info.apk", R.raw.install_complete_package_info);
+                "install_complete_package_info.apk", R.raw.install_complete_package_info,
+                converter);
         String packageName = "com.android.frameworks.coretests.install_complete_package_info";
 
         assertEquals(packageName, p.packageName);
@@ -344,10 +381,37 @@
                 packageName, PermissionInfo.PROTECTION_NORMAL, p.permissions.get(0));
 
         assertOneComponentOfEachType("com.android.frameworks.coretests.Test%s", p);
+
+        assertMetadata(p.mAppMetaData,
+                "key1", "value1",
+                "key2", "this_is_app");
+        assertMetadata(p.activities.get(0).metaData,
+                "key1", "value1",
+                "key2", "this_is_activity");
+        assertMetadata(p.services.get(0).metaData,
+                "key1", "value1",
+                "key2", "this_is_service");
+        assertMetadata(p.receivers.get(0).metaData,
+                "key1", "value1",
+                "key2", "this_is_receiver");
+        assertMetadata(p.providers.get(0).metaData,
+                "key1", "value1",
+                "key2", "this_is_provider");
+    }
+
+    /**
+     * Determines if the current device supports multi-package APKs.
+     */
+    private boolean supportsMultiPackageApk() {
+        return SystemProperties.getBoolean("persist.sys.child_packages_enabled", false);
     }
 
     @Test
     public void testMultiPackageComponents() throws Exception {
+        // TODO(gboyer): Remove once we decide to launch multi-package APKs.
+        if (!supportsMultiPackageApk()) {
+            return;
+        }
         String parentName = "com.android.frameworks.coretests.install_multi_package";
         String firstChildName =
                 "com.android.frameworks.coretests.install_multi_package.first_child";
diff --git a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
index f97d51d..335cea7 100644
--- a/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
+++ b/core/tests/coretests/src/android/database/DatabaseGeneralTest.java
@@ -1058,7 +1058,7 @@
         mDatabase.close();
         SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder()
                 .setLookasideConfig(0, 0).build();
-        mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile.getPath(), params);
+        mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile, params);
         verifyLookasideStats(true);
     }
 
@@ -1193,7 +1193,7 @@
         mDatabase.close();
         SQLiteDatabase.OpenParams params = new SQLiteDatabase.OpenParams.Builder()
                 .setIdleConnectionTimeout(1000).build();
-        mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile.getPath(), params);
+        mDatabase = SQLiteDatabase.openDatabase(mDatabaseFile, params);
         // Wait a bit and check that connection is still open
         Thread.sleep(100);
         String output = executeShellCommand("dumpsys dbinfo " + getContext().getPackageName());
diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java
new file mode 100644
index 0000000..9fcf96d
--- /dev/null
+++ b/core/tests/coretests/src/android/os/BundleTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for bundle that requires accessing hidden APS.  Tests that can be written only with
+ * public APIs should go in the CTS counterpart.
+ *
+ * Run with:
+ * bit FrameworksCoreTests:android.os.BundleTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BundleTest {
+    /**
+     * Create a test bundle, parcel it and return the parcel.
+     */
+    private Parcel createBundleParcel(boolean withFd) throws Exception {
+        final Bundle source = new Bundle();
+        source.putString("string", "abc");
+        source.putInt("int", 1);
+        if (withFd) {
+            ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+            pipe[1].close();
+            source.putParcelable("fd", pipe[0]);
+        }
+        final Parcel p = Parcel.obtain();
+        // Don't use p.writeParcelabe(), which would write the creator, which we don't need.
+        source.writeToParcel(p, 0);
+        p.setDataPosition(0);
+
+        return p;
+    }
+
+    /**
+     * Verify a bundle generated by {@link #createBundleParcel(boolean)}.
+     */
+    private void checkBundle(Bundle b, boolean withFd) {
+        // First, do the checks without actually unparceling the bundle.
+        // (Note looking into the contents will unparcel a bundle, so we'll do it later.)
+        assertTrue("mParcelledData shouldn't be null here.", b.isParcelled());
+
+        // Make sure FLAG_HAS_FDS and FLAG_HAS_FDS_KNOWN are set/cleared properly.
+        if (withFd) {
+            // FLAG_HAS_FDS and FLAG_HAS_FDS_KNOWN should both be set.
+            assertEquals(Bundle.FLAG_HAS_FDS | Bundle.FLAG_HAS_FDS_KNOWN,
+                    b.mFlags & (Bundle.FLAG_HAS_FDS | Bundle.FLAG_HAS_FDS_KNOWN));
+        } else {
+            // FLAG_HAS_FDS_KNOWN should be set, bot not FLAG_HAS_FDS.
+            assertEquals(Bundle.FLAG_HAS_FDS_KNOWN,
+                    b.mFlags & (Bundle.FLAG_HAS_FDS | Bundle.FLAG_HAS_FDS_KNOWN));
+        }
+
+        // Then, check the contents.
+        assertEquals("abc", b.getString("string"));
+        assertEquals(1, b.getInt("int"));
+
+        // Make sure FLAG_HAS_FDS and FLAG_HAS_FDS_KNOWN are set/cleared properly.
+        if (withFd) {
+            assertEquals(ParcelFileDescriptor.class, b.getParcelable("fd").getClass());
+            assertEquals(3, b.keySet().size());
+        } else {
+            assertEquals(2, b.keySet().size());
+        }
+        assertFalse(b.isParcelled());
+    }
+
+    @Test
+    public void testCreateFromParcel() throws Exception {
+        boolean withFd;
+        Parcel p;
+        Bundle b;
+        int length;
+
+        withFd = false;
+
+        // new Bundle with p
+        p = createBundleParcel(withFd);
+        checkBundle(new Bundle(p), withFd);
+        p.recycle();
+
+        // new Bundle with p and length
+        p = createBundleParcel(withFd);
+        length = p.readInt();
+        checkBundle(new Bundle(p, length), withFd);
+        p.recycle();
+
+        // readFromParcel()
+        p = createBundleParcel(withFd);
+        b = new Bundle();
+        b.readFromParcel(p);
+        checkBundle(b, withFd);
+        p.recycle();
+
+        // Same test with FDs.
+        withFd = true;
+
+        // new Bundle with p
+        p = createBundleParcel(withFd);
+        checkBundle(new Bundle(p), withFd);
+        p.recycle();
+
+        // new Bundle with p and length
+        p = createBundleParcel(withFd);
+        length = p.readInt();
+        checkBundle(new Bundle(p, length), withFd);
+        p.recycle();
+
+        // readFromParcel()
+        p = createBundleParcel(withFd);
+        b = new Bundle();
+        b.readFromParcel(p);
+        checkBundle(b, withFd);
+        p.recycle();
+    }
+}
diff --git a/core/tests/coretests/src/android/text/format/DateUtilsTest.java b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
index 6063e1a..0920d1e 100644
--- a/core/tests/coretests/src/android/text/format/DateUtilsTest.java
+++ b/core/tests/coretests/src/android/text/format/DateUtilsTest.java
@@ -33,6 +33,7 @@
 import java.util.Calendar;
 import java.util.Date;
 import java.util.Locale;
+import java.util.TimeZone;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -40,16 +41,20 @@
 
     private static final LocaleList LOCALE_LIST_US = new LocaleList(Locale.US);
     private LocaleList mOriginalLocales;
+    private TimeZone mOriginalTimeZone;
 
     @Before
     public void setup() {
         mOriginalLocales = Resources.getSystem().getConfiguration().getLocales();
         setLocales(LOCALE_LIST_US);
+        mOriginalTimeZone = TimeZone.getDefault();
+        TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
     }
 
     @After
     public void teardown() {
         setLocales(mOriginalLocales);
+        TimeZone.setDefault(mOriginalTimeZone);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/view/ViewTransientState.java b/core/tests/coretests/src/android/view/ViewTransientState.java
new file mode 100644
index 0000000..206ff81
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ViewTransientState.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2007 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.view;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.frameworks.coretests.R;
+
+/**
+ * Exercise set View's transient state
+ */
+public class ViewTransientState extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.view_transient_state);
+    }
+}
diff --git a/core/tests/coretests/src/android/view/ViewTransientStateTest.java b/core/tests/coretests/src/android/view/ViewTransientStateTest.java
new file mode 100644
index 0000000..36ea01d
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ViewTransientStateTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 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.view;
+
+import android.app.Activity;
+import android.test.ActivityInstrumentationTestCase;
+import android.test.UiThreadTest;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import com.android.frameworks.coretests.R;
+
+import static org.junit.Assert.assertFalse;
+
+/**
+ * Exercise set View's transient state
+ */
+public class ViewTransientStateTest extends ActivityInstrumentationTestCase<ViewTransientState> {
+
+    View mP1;
+    View mP2;
+    View mP3;
+
+    public ViewTransientStateTest() {
+        super("com.android.frameworks.coretests", ViewTransientState.class);
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        final Activity a = getActivity();
+        mP1 = a.findViewById(R.id.p1);
+        mP2 = a.findViewById(R.id.p2);
+        mP3 = a.findViewById(R.id.p3);
+    }
+
+    @UiThreadTest
+    @MediumTest
+    public void testSetTransientState1() throws Exception {
+        mP3.setHasTransientState(true);
+        mP2.setHasTransientState(true);
+        mP3.setHasTransientState(false);
+        mP2.setHasTransientState(false);
+        assertFalse(mP3.hasTransientState());
+        assertFalse(mP2.hasTransientState());
+        assertFalse(mP1.hasTransientState());
+    }
+
+    @UiThreadTest
+    @MediumTest
+    public void testSetTransientState2() throws Exception {
+        mP3.setHasTransientState(true);
+        mP2.setHasTransientState(true);
+        mP2.setHasTransientState(false);
+        mP3.setHasTransientState(false);
+        assertFalse(mP3.hasTransientState());
+        assertFalse(mP2.hasTransientState());
+        assertFalse(mP1.hasTransientState());
+    }
+
+    @UiThreadTest
+    @MediumTest
+    public void testSetTransientState3() throws Exception {
+        mP2.setHasTransientState(true);
+        mP3.setHasTransientState(true);
+        mP3.setHasTransientState(false);
+        mP2.setHasTransientState(false);
+        assertFalse(mP3.hasTransientState());
+        assertFalse(mP2.hasTransientState());
+        assertFalse(mP1.hasTransientState());
+    }
+
+    @UiThreadTest
+    @MediumTest
+    public void testSetTransientState4() throws Exception {
+        mP2.setHasTransientState(true);
+        mP3.setHasTransientState(true);
+        mP2.setHasTransientState(false);
+        mP3.setHasTransientState(false);
+        assertFalse(mP3.hasTransientState());
+        assertFalse(mP2.hasTransientState());
+        assertFalse(mP1.hasTransientState());
+    }
+}
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
index 29447ed..2a6c22e 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationManagerTest.java
@@ -32,9 +32,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.List;
-import java.util.Locale;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class TextClassificationManagerTest {
@@ -177,20 +174,6 @@
     }
 
     @Test
-    public void testLanguageDetection() {
-        if (isTextClassifierDisabled()) return;
-
-        String text = "This is a piece of English text";
-        assertThat(mTcm.detectLanguages(text), isDetectedLanguage("en"));
-
-        text = "Das ist ein deutscher Text";
-        assertThat(mTcm.detectLanguages(text), isDetectedLanguage("de"));
-
-        text = "これは日本語のテキストです";
-        assertThat(mTcm.detectLanguages(text), isDetectedLanguage("ja"));
-    }
-
-    @Test
     public void testSetTextClassifier() {
         TextClassifier classifier = mock(TextClassifier.class);
         mTcm.setTextClassifier(classifier);
@@ -270,30 +253,4 @@
             }
         };
     }
-
-    private static Matcher<List<TextLanguage>> isDetectedLanguage(final String language) {
-        return new BaseMatcher<List<TextLanguage>>() {
-            @Override
-            public boolean matches(Object o) {
-                if (o instanceof List) {
-                    List languages = (List) o;
-                    if (!languages.isEmpty()) {
-                        Object o1 = languages.get(0);
-                        if (o1 instanceof TextLanguage) {
-                            TextLanguage lang = (TextLanguage) o1;
-                            return lang.getLanguageCount() > 0
-                                    && new Locale(language).getLanguage()
-                                            .equals(lang.getLanguage(0).getLanguage());
-                        }
-                    }
-                }
-                return false;
-            }
-
-            @Override
-            public void describeTo(Description description) {
-                description.appendValue(String.format("%s", language));
-            }
-        };
-    }
 }
diff --git a/core/tests/coretests/src/android/widget/TextViewActivity.java b/core/tests/coretests/src/android/widget/TextViewActivity.java
index 03358a8..5710476 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivity.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivity.java
@@ -16,62 +16,19 @@
 
 package android.widget;
 
-import com.android.frameworks.coretests.R;
-
 import android.app.Activity;
 import android.os.Bundle;
-import android.os.SystemClock;
-import android.util.Log;
+
+import com.android.frameworks.coretests.R;
 
 /**
  * An activity for testing the TextView widget.
- *
- * This class is copied from {@link android.text.method.cts.KeyListenerCtsActivity} in
- * CtsTextTestCase.  The original class is located at
- * cts/tests/tests/text/src/android/text/method/cts/KeyListenerCtsActivity.java
  */
 public class TextViewActivity extends Activity {
-    private boolean mHasWindowFocus = false;
-    private Object mHasWindowFocusLock = new Object();
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_text_view);
     }
-
-    @Override
-    public void onWindowFocusChanged(boolean hasFocus) {
-        super.onWindowFocusChanged(hasFocus);
-        if (!hasFocus) {
-            Log.w("TextViewActivity", "TextViewActivity lost window focus");
-        }
-        synchronized(mHasWindowFocusLock) {
-            mHasWindowFocus = hasFocus;
-            mHasWindowFocusLock.notify();
-        }
-    }
-
-    /**
-     * Blocks the calling thread until the {@link KeyListenerCtsActivity} has window focus or the
-     * specified duration (in milliseconds) has passed.
-     */
-    public boolean waitForWindowFocus(long durationMillis) {
-        long elapsedMillis = SystemClock.elapsedRealtime();
-        synchronized(mHasWindowFocusLock) {
-            mHasWindowFocus = hasWindowFocus();
-            while (!mHasWindowFocus && durationMillis > 0) {
-                long newElapsedMillis = SystemClock.elapsedRealtime();
-                durationMillis -= (newElapsedMillis - elapsedMillis);
-                elapsedMillis = newElapsedMillis;
-                if (durationMillis > 0) {
-                    try {
-                        mHasWindowFocusLock.wait(durationMillis);
-                    } catch (InterruptedException e) {
-                    }
-                }
-            }
-            return mHasWindowFocus;
-        }
-    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
new file mode 100644
index 0000000..e69cd5d
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsCpuTimesTest.java
@@ -0,0 +1,774 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.os;
+
+import static android.os.BatteryStats.STATS_SINCE_CHARGED;
+import static android.os.BatteryStats.WAKE_TYPE_PARTIAL;
+import static android.os.Process.FIRST_APPLICATION_UID;
+import static android.os.Process.FIRST_ISOLATED_UID;
+
+import static com.android.internal.os.BatteryStatsImpl.WAKE_LOCK_WEIGHT;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.BatteryStats;
+import android.os.Process;
+import android.os.UserHandle;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseLongArray;
+
+import com.android.internal.util.ArrayUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * To run the tests, use
+ *
+ * runtest -c com.android.internal.os.BatteryStatsCpuTimesTest frameworks-core
+ *
+ * or
+ *
+ * Build: m FrameworksCoreTests
+ * Install: adb install -r \
+ *     ${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreTests/FrameworksCoreTests.apk
+ * Run: adb shell am instrument -e class com.android.internal.os.BatteryStatsCpuTimesTest -w \
+ *     com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
+ *
+ * or
+ *
+ * bit FrameworksCoreTests:com.android.internal.os.BatteryStatsCpuTimesTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BatteryStatsCpuTimesTest {
+    @Mock KernelUidCpuTimeReader mKernelUidCpuTimeReader;
+    @Mock KernelUidCpuFreqTimeReader mKernelUidCpuFreqTimeReader;
+    @Mock BatteryStatsImpl.UserInfoProvider mUserInfoProvider;
+    @Mock PowerProfile mPowerProfile;
+
+    private MockClocks mClocks;
+    private MockBatteryStatsImpl mBatteryStatsImpl;
+    private KernelCpuSpeedReader[] mKernelCpuSpeedReaders;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mClocks = new MockClocks();
+        mBatteryStatsImpl = new MockBatteryStatsImpl(mClocks)
+                .setKernelUidCpuTimeReader(mKernelUidCpuTimeReader)
+                .setKernelUidCpuFreqTimeReader(mKernelUidCpuFreqTimeReader)
+                .setUserInfoProvider(mUserInfoProvider);
+    }
+
+    @Test
+    public void testUpdateCpuTimeLocked() {
+        // PRECONDITIONS
+        mBatteryStatsImpl.setPowerProfile(mPowerProfile);
+        mBatteryStatsImpl.setOnBatteryInternal(false);
+        final int numClusters = 3;
+        initKernelCpuSpeedReaders(numClusters);
+
+        // RUN
+        mBatteryStatsImpl.updateCpuTimeLocked(true);
+
+        // VERIFY
+        verify(mKernelUidCpuTimeReader).readDelta(null);
+        verify(mKernelUidCpuFreqTimeReader).readDelta(null);
+        for (int i = 0; i < numClusters; ++i) {
+            verify(mKernelCpuSpeedReaders[i]).readDelta();
+        }
+
+        // Prepare for next test
+        Mockito.reset(mUserInfoProvider, mKernelUidCpuFreqTimeReader, mKernelUidCpuTimeReader);
+        for (int i = 0; i < numClusters; ++i) {
+            Mockito.reset(mKernelCpuSpeedReaders[i]);
+        }
+
+        // PRECONDITIONS
+        mBatteryStatsImpl.setOnBatteryInternal(true);
+
+        // RUN
+        mBatteryStatsImpl.updateCpuTimeLocked(true);
+
+        // VERIFY
+        verify(mUserInfoProvider).refreshUserIds();
+        verify(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
+        verify(mKernelUidCpuFreqTimeReader).readDelta(
+                any(KernelUidCpuFreqTimeReader.Callback.class));
+        for (int i = 0; i < numClusters; ++i) {
+            verify(mKernelCpuSpeedReaders[i]).readDelta();
+        }
+    }
+
+    @Test
+    public void testMarkPartialTimersAsEligible() {
+        // PRECONDITIONS
+        final ArrayList<BatteryStatsImpl.StopwatchTimer> partialTimers = getPartialTimers(
+                10032, 10042, 10052);
+        final ArrayList<BatteryStatsImpl.StopwatchTimer> lastPartialTimers
+                = new ArrayList<>(partialTimers);
+        mBatteryStatsImpl.setPartialTimers(partialTimers);
+        mBatteryStatsImpl.setLastPartialTimers(lastPartialTimers);
+        final boolean[] inList = {false, true, false};
+        for (int i = 0; i < partialTimers.size(); ++i) {
+            partialTimers.get(i).mInList = inList[i];
+        }
+
+        // RUN
+        mBatteryStatsImpl.markPartialTimersAsEligible();
+
+        // VERIFY
+        assertTrue(ArrayUtils.referenceEquals(partialTimers, lastPartialTimers));
+        for (int i = 0; i < partialTimers.size(); ++i) {
+            assertTrue("Timer id=" + i, partialTimers.get(i).mInList);
+        }
+
+        // PRECONDITIONS
+        partialTimers.addAll(getPartialTimers(10077, 10099));
+        partialTimers.remove(1 /* index */);
+
+        // RUN
+        mBatteryStatsImpl.markPartialTimersAsEligible();
+
+        // VERIFY
+        assertTrue(ArrayUtils.referenceEquals(partialTimers, lastPartialTimers));
+        for (int i = 0; i < partialTimers.size(); ++i) {
+            assertTrue("Timer id=" + i, partialTimers.get(i).mInList);
+        }
+    }
+
+    @Test
+    public void testUpdateClusterSpeedTimes() {
+        // PRECONDITIONS
+
+        // Call updateTimeBasesLocked before setting the power profile to avoid execution of
+        // BatteryStatsImpl.updateCpuTimeLocked.
+        mBatteryStatsImpl.updateTimeBasesLocked(true, false, 0, 0);
+        mBatteryStatsImpl.setPowerProfile(mPowerProfile);
+        final long[][] clusterSpeedTimesMs = {{20, 30}, {40, 50, 60}};
+        initKernelCpuSpeedReaders(clusterSpeedTimesMs.length);
+        for (int i = 0; i < clusterSpeedTimesMs.length; ++i) {
+            when(mKernelCpuSpeedReaders[i].readDelta()).thenReturn(clusterSpeedTimesMs[i]);
+        }
+        when(mPowerProfile.getNumCpuClusters()).thenReturn(clusterSpeedTimesMs.length);
+        for (int i = 0; i < clusterSpeedTimesMs.length; ++i) {
+            when(mPowerProfile.getNumSpeedStepsInCpuCluster(i))
+                    .thenReturn(clusterSpeedTimesMs[i].length);
+        }
+        final SparseLongArray updatedUids = new SparseLongArray();
+        final int[] testUids = {10012, 10014, 10016};
+        final int[] cpuTimeUs = {89, 31, 43};
+        for (int i = 0; i < testUids.length; ++i) {
+            updatedUids.put(testUids[i], cpuTimeUs[i]);
+        }
+
+        // RUN
+        mBatteryStatsImpl.updateClusterSpeedTimes(updatedUids);
+
+        // VERIFY
+        int totalClustersTimeMs = 0;
+        for (int i = 0; i < clusterSpeedTimesMs.length; ++i) {
+            for (int j = 0; j < clusterSpeedTimesMs[i].length; ++j) {
+                totalClustersTimeMs += clusterSpeedTimesMs[i][j];
+            }
+        }
+        for (int i = 0; i < testUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            assertNotNull("No entry for uid=" + testUids[i], u);
+            for (int cluster = 0; cluster < clusterSpeedTimesMs.length; ++cluster) {
+                for (int speed = 0; speed < clusterSpeedTimesMs[cluster].length; ++speed) {
+                    assertEquals("Uid=" + testUids[i] + ", cluster=" + cluster + ", speed=" + speed,
+                            cpuTimeUs[i] * clusterSpeedTimesMs[cluster][speed]
+                                    / totalClustersTimeMs,
+                            u.getTimeAtCpuSpeed(cluster, speed, STATS_SINCE_CHARGED));
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testReadKernelUidCpuTimesLocked() {
+        //PRECONDITIONS
+        mBatteryStatsImpl.updateTimeBasesLocked(true, false, 0, 0);
+        final int testUserId = 11;
+        when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
+        final int[] testUids = getUids(testUserId, new int[] {
+                FIRST_APPLICATION_UID + 22,
+                FIRST_APPLICATION_UID + 27,
+                FIRST_APPLICATION_UID + 33
+        });
+        final long[][] uidTimesUs = {
+                {12, 34}, {34897394, 3123983}, {79775429834l, 8430434903489l}
+        };
+        doAnswer(invocation -> {
+            final KernelUidCpuTimeReader.Callback callback =
+                    (KernelUidCpuTimeReader.Callback) invocation.getArguments()[0];
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuTime(testUids[i], uidTimesUs[i][0], uidTimesUs[i][1]);
+            }
+            return null;
+        }).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
+
+        // RUN
+        final SparseLongArray updatedUids = new SparseLongArray();
+        mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, updatedUids);
+
+        // VERIFY
+        for (int i = 0; i < testUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            assertNotNull("No entry for uid=" + testUids[i], u);
+            assertEquals("Unexpected user cpu time for uid=" + testUids[i],
+                    uidTimesUs[i][0], u.getUserCpuTimeUs(STATS_SINCE_CHARGED));
+            assertEquals("Unexpected system cpu time for uid=" + testUids[i],
+                    uidTimesUs[i][1], u.getSystemCpuTimeUs(STATS_SINCE_CHARGED));
+
+            assertEquals("Unexpected entry in updated uids for uid=" + testUids[i],
+                    uidTimesUs[i][0] + uidTimesUs[i][1], updatedUids.get(testUids[i]));
+            updatedUids.delete(testUids[i]);
+        }
+        assertEquals("Updated uids: " + updatedUids, 0, updatedUids.size());
+
+        // Repeat the test with a null updatedUids
+
+        // PRECONDITIONS
+        final long[][] deltasUs = {
+                {9379, 3332409833484l}, {493247, 723234}, {3247819, 123348}
+        };
+        doAnswer(invocation -> {
+            final KernelUidCpuTimeReader.Callback callback =
+                    (KernelUidCpuTimeReader.Callback) invocation.getArguments()[0];
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuTime(testUids[i], deltasUs[i][0], deltasUs[i][1]);
+            }
+            return null;
+        }).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
+
+        // RUN
+        mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null);
+
+        // VERIFY
+        for (int i = 0; i < testUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            assertNotNull("No entry for uid=" + testUids[i], u);
+            assertEquals("Unexpected user cpu time for uid=" + testUids[i],
+                    uidTimesUs[i][0] + deltasUs[i][0], u.getUserCpuTimeUs(STATS_SINCE_CHARGED));
+            assertEquals("Unexpected system cpu time for uid=" + testUids[i],
+                    uidTimesUs[i][1] + deltasUs[i][1], u.getSystemCpuTimeUs(STATS_SINCE_CHARGED));
+        }
+    }
+
+    @Test
+    public void testReadKernelUidCpuTimesLocked_isolatedUid() {
+        //PRECONDITIONS
+        mBatteryStatsImpl.updateTimeBasesLocked(true, false, 0, 0);
+        final int testUserId = 11;
+        when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
+        final int isolatedAppId = FIRST_ISOLATED_UID + 27;
+        final int isolatedUid = UserHandle.getUid(testUserId, isolatedAppId);
+        final int[] testUids = getUids(testUserId, new int[] {
+                FIRST_APPLICATION_UID + 22,
+                isolatedAppId,
+                FIRST_APPLICATION_UID + 33
+        });
+        final long[][] uidTimesUs = {
+                {12, 34}, {34897394, 3123983}, {79775429834l, 8430434903489l}
+        };
+        doAnswer(invocation -> {
+            final KernelUidCpuTimeReader.Callback callback =
+                    (KernelUidCpuTimeReader.Callback) invocation.getArguments()[0];
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuTime(testUids[i], uidTimesUs[i][0], uidTimesUs[i][1]);
+            }
+            return null;
+        }).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
+
+        // RUN
+        mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null);
+
+        // VERIFY
+        for (int i = 0; i < testUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            if (UserHandle.isIsolated(testUids[i])) {
+                assertNull("There shouldn't be an entry for isolated uid=" + testUids[i], u);
+                continue;
+            }
+            assertNotNull("No entry for uid=" + testUids[i], u);
+            assertEquals("Unexpected user cpu time for uid=" + testUids[i],
+                    uidTimesUs[i][0], u.getUserCpuTimeUs(STATS_SINCE_CHARGED));
+            assertEquals("Unexpected system cpu time for uid=" + testUids[i],
+                    uidTimesUs[i][1], u.getSystemCpuTimeUs(STATS_SINCE_CHARGED));
+        }
+        verify(mKernelUidCpuTimeReader).removeUid(isolatedUid);
+
+        // Add an isolated uid mapping and repeat the test.
+
+        // PRECONDITIONS
+        final int ownerUid = UserHandle.getUid(testUserId, FIRST_APPLICATION_UID + 42);
+        mBatteryStatsImpl.addIsolatedUidLocked(isolatedUid, ownerUid);
+        final long[][] deltasUs = {
+                {9379, 3332409833484l}, {493247, 723234}, {3247819, 123348}
+        };
+        doAnswer(invocation -> {
+            final KernelUidCpuTimeReader.Callback callback =
+                    (KernelUidCpuTimeReader.Callback) invocation.getArguments()[0];
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuTime(testUids[i], deltasUs[i][0], deltasUs[i][1]);
+            }
+            return null;
+        }).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
+
+        // RUN
+        mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null);
+
+        // VERIFY
+        for (int i = 0; i < testUids.length; ++i) {
+            BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            final long expectedUserTimeUs;
+            final long expectedSystemTimeUs;
+            if (UserHandle.isIsolated(testUids[i])) {
+                assertNull("There shouldn't be an entry for isolated uid=" + testUids[i], u);
+                // Since we added a mapping, an entry should've been created for owner uid.
+                u = mBatteryStatsImpl.getUidStats().get(ownerUid);
+                expectedUserTimeUs = deltasUs[i][0];
+                expectedSystemTimeUs = deltasUs[i][1];
+                assertNotNull("No entry for owner uid=" + ownerUid, u);
+            } else {
+                assertNotNull("No entry for uid=" + testUids[i], u);
+                expectedUserTimeUs = uidTimesUs[i][0] + deltasUs[i][0];
+                expectedSystemTimeUs = uidTimesUs[i][1] + deltasUs[i][1];
+            }
+            assertEquals("Unexpected user cpu time for uid=" + testUids[i],
+                    expectedUserTimeUs, u.getUserCpuTimeUs(STATS_SINCE_CHARGED));
+            assertEquals("Unexpected system cpu time for uid=" + testUids[i],
+                    expectedSystemTimeUs, u.getSystemCpuTimeUs(STATS_SINCE_CHARGED));
+        }
+    }
+
+    @Test
+    public void testReadKernelUidCpuTimesLocked_invalidUid() {
+        //PRECONDITIONS
+        mBatteryStatsImpl.updateTimeBasesLocked(true, false, 0, 0);
+        final int testUserId = 11;
+        final int invalidUserId = 15;
+        final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99);
+        when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
+        when(mUserInfoProvider.exists(invalidUserId)).thenReturn(false);
+        final int[] testUids = getUids(testUserId, new int[] {
+                FIRST_APPLICATION_UID + 22,
+                FIRST_APPLICATION_UID + 27,
+                FIRST_APPLICATION_UID + 33
+        });
+        final long[][] uidTimesUs = {
+                {12, 34}, {34897394, 3123983}, {79775429834l, 8430434903489l}
+        };
+        doAnswer(invocation -> {
+            final KernelUidCpuTimeReader.Callback callback =
+                    (KernelUidCpuTimeReader.Callback) invocation.getArguments()[0];
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuTime(testUids[i], uidTimesUs[i][0], uidTimesUs[i][1]);
+            }
+            // And one for the invalid uid
+            callback.onUidCpuTime(invalidUid, 3879, 239);
+            return null;
+        }).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
+
+        // RUN
+        mBatteryStatsImpl.readKernelUidCpuTimesLocked(null, null);
+
+        // VERIFY
+        for (int i = 0; i < testUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            assertNotNull("No entry for uid=" + testUids[i], u);
+            assertEquals("Unexpected user cpu time for uid=" + testUids[i],
+                    uidTimesUs[i][0], u.getUserCpuTimeUs(STATS_SINCE_CHARGED));
+            assertEquals("Unexpected system cpu time for uid=" + testUids[i],
+                    uidTimesUs[i][1], u.getSystemCpuTimeUs(STATS_SINCE_CHARGED));
+        }
+        assertNull("There shouldn't be an entry for invalid uid=" + invalidUid,
+                mBatteryStatsImpl.getUidStats().get(invalidUid));
+        verify(mKernelUidCpuTimeReader).removeUid(invalidUid);
+    }
+
+    @Test
+    public void testReadKernelUidCpuTimesLocked_withPartialTimers() {
+        //PRECONDITIONS
+        mBatteryStatsImpl.updateTimeBasesLocked(true, false, 0, 0);
+        final int testUserId = 11;
+        when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
+        final int[] testUids = getUids(testUserId, new int[] {
+                FIRST_APPLICATION_UID + 22,
+                FIRST_APPLICATION_UID + 27,
+                FIRST_APPLICATION_UID + 33
+        });
+        final int[] partialTimerUids = {
+                UserHandle.getUid(testUserId, FIRST_APPLICATION_UID + 48),
+                UserHandle.getUid(testUserId, FIRST_APPLICATION_UID + 10)
+        };
+        final ArrayList<BatteryStatsImpl.StopwatchTimer> partialTimers
+                = getPartialTimers(partialTimerUids);
+        final long[][] uidTimesUs = {
+                {12, 34}, {3394, 3123}, {7977, 80434}
+        };
+        doAnswer(invocation -> {
+            final KernelUidCpuTimeReader.Callback callback =
+                    (KernelUidCpuTimeReader.Callback) invocation.getArguments()[0];
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuTime(testUids[i], uidTimesUs[i][0], uidTimesUs[i][1]);
+            }
+            return null;
+        }).when(mKernelUidCpuTimeReader).readDelta(any(KernelUidCpuTimeReader.Callback.class));
+
+        // RUN
+        final SparseLongArray updatedUids = new SparseLongArray();
+        mBatteryStatsImpl.readKernelUidCpuTimesLocked(partialTimers, updatedUids);
+
+        // VERIFY
+        long totalUserTimeUs = 0;
+        long totalSystemTimeUs = 0;
+        for (int i = 0; i < testUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            assertNotNull("No entry for uid=" + testUids[i], u);
+            final long expectedUserTimeUs = uidTimesUs[i][0] * WAKE_LOCK_WEIGHT / 100;
+            final long expectedSystemTimeUs = uidTimesUs[i][1] * WAKE_LOCK_WEIGHT / 100;
+            assertEquals("Unexpected user cpu time for uid=" + testUids[i],
+                    expectedUserTimeUs, u.getUserCpuTimeUs(STATS_SINCE_CHARGED));
+            assertEquals("Unexpected system cpu time for uid=" + testUids[i],
+                    expectedSystemTimeUs, u.getSystemCpuTimeUs(STATS_SINCE_CHARGED));
+            assertEquals("Unexpected entry in updated uids for uid=" + testUids[i],
+                    expectedUserTimeUs + expectedSystemTimeUs, updatedUids.get(testUids[i]));
+            updatedUids.delete(testUids[i]);
+            totalUserTimeUs += uidTimesUs[i][0];
+            totalSystemTimeUs += uidTimesUs[i][1];
+        }
+
+        totalUserTimeUs = totalUserTimeUs * (100 - WAKE_LOCK_WEIGHT) / 100;
+        totalSystemTimeUs = totalSystemTimeUs * (100 - WAKE_LOCK_WEIGHT) / 100;
+        for (int i = 0; i < partialTimerUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(partialTimerUids[i]);
+            assertNotNull("No entry for partial timer uid=" + partialTimerUids[i], u);
+            final long expectedUserTimeUs = totalUserTimeUs / (partialTimerUids.length - i);
+            final long expectedSystemTimeUs = totalSystemTimeUs / (partialTimerUids.length - i);
+            assertEquals("Unexpected user cpu time for partial timer uid=" + partialTimerUids[i],
+                    expectedUserTimeUs, u.getUserCpuTimeUs(STATS_SINCE_CHARGED));
+            assertEquals("Unexpected system cpu time for partial timer uid=" + partialTimerUids[i],
+                    expectedSystemTimeUs, u.getSystemCpuTimeUs(STATS_SINCE_CHARGED));
+            assertEquals("Unexpected entry in updated uids for partial timer uid="
+                            + partialTimerUids[i],
+                    expectedUserTimeUs + expectedSystemTimeUs,
+                    updatedUids.get(partialTimerUids[i]));
+            updatedUids.delete(partialTimerUids[i]);
+            totalUserTimeUs -= expectedUserTimeUs;
+            totalSystemTimeUs -= expectedSystemTimeUs;
+
+            final BatteryStats.Uid.Proc proc = u.getProcessStats().get("*wakelock*");
+            assertEquals("Unexpected user cpu time for *wakelock* in uid=" + partialTimerUids[i],
+                    expectedUserTimeUs / 1000, proc.getUserTime(STATS_SINCE_CHARGED));
+            assertEquals("Unexpected system cpu time for *wakelock* in uid=" + partialTimerUids[i],
+                    expectedSystemTimeUs / 1000, proc.getSystemTime(STATS_SINCE_CHARGED));
+        }
+        assertEquals(0, totalUserTimeUs);
+        assertEquals(0, totalSystemTimeUs);
+        assertEquals("Updated uids: " + updatedUids, 0, updatedUids.size());
+    }
+
+    @Test
+    public void testReadKernelUidCpuFreqTimesLocked() {
+        // PRECONDITIONS
+        mBatteryStatsImpl.updateTimeBasesLocked(true, false, 0, 0);
+
+        final int testUserId = 11;
+        when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
+        final int[] testUids = getUids(testUserId, new int[] {
+            FIRST_APPLICATION_UID + 22,
+            FIRST_APPLICATION_UID + 27,
+            FIRST_APPLICATION_UID + 33
+        });
+        final long[] freqs = {1, 12, 123, 12, 1234};
+        final long[][] uidTimesMs = {
+                {4, 10, 5, 9, 4},
+                {5, 1, 12, 2, 10},
+                {8, 25, 3, 0, 42}
+        };
+        doAnswer(invocation -> {
+            final KernelUidCpuFreqTimeReader.Callback callback =
+                    (KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
+            callback.onCpuFreqs(freqs);
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]);
+            }
+            return null;
+        }).when(mKernelUidCpuFreqTimeReader).readDelta(
+                any(KernelUidCpuFreqTimeReader.Callback.class));
+
+        // RUN
+        mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked();
+
+        // VERIFY
+        assertArrayEquals("Unexpected cpu freqs", freqs, mBatteryStatsImpl.getCpuFreqs());
+        for (int i = 0; i < testUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            assertNotNull("No entry for uid=" + testUids[i], u);
+
+            assertArrayEquals("Unexpected cpu times for uid=" + testUids[i],
+                    uidTimesMs[i], u.getCpuFreqTimes(STATS_SINCE_CHARGED));
+            assertNull("Unexpected screen-off cpu times for uid=" + testUids[i],
+                    u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED));
+        }
+
+        // Repeat the test when the screen is off.
+
+        // PRECONDITIONS
+        mBatteryStatsImpl.updateTimeBasesLocked(true, true, 0, 0);
+        final long[][] deltasMs = {
+                {3, 12, 55, 100, 32},
+                {3248327490475l, 232349349845043l, 123, 2398, 0},
+                {43, 3345, 2143, 123, 4554}
+        };
+        doAnswer(invocation -> {
+            final KernelUidCpuFreqTimeReader.Callback callback =
+                    (KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
+            callback.onCpuFreqs(freqs);
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuFreqTime(testUids[i], deltasMs[i]);
+            }
+            return null;
+        }).when(mKernelUidCpuFreqTimeReader).readDelta(
+                any(KernelUidCpuFreqTimeReader.Callback.class));
+
+        // RUN
+        mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked();
+
+        // VERIFY
+        assertArrayEquals("Unexpected cpu freqs", freqs, mBatteryStatsImpl.getCpuFreqs());
+        for (int i = 0; i < testUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            assertNotNull("No entry for uid=" + testUids[i], u);
+
+            assertArrayEquals("Unexpected cpu times for uid=" + testUids[i],
+                    sum(uidTimesMs[i], deltasMs[i]), u.getCpuFreqTimes(STATS_SINCE_CHARGED));
+            assertArrayEquals("Unexpected screen-off cpu times for uid=" + testUids[i],
+                    deltasMs[i], u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED));
+        }
+    }
+
+    @Test
+    public void testReadKernelUidCpuFreqTimesLocked_isolatedUid() {
+        // PRECONDITIONS
+        mBatteryStatsImpl.updateTimeBasesLocked(true, false, 0, 0);
+
+        final int testUserId = 11;
+        when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
+        final int isolatedAppId = FIRST_ISOLATED_UID + 27;
+        final int isolatedUid = UserHandle.getUid(testUserId, isolatedAppId);
+        final int[] testUids = getUids(testUserId, new int[] {
+                FIRST_APPLICATION_UID + 22,
+                isolatedAppId,
+                FIRST_APPLICATION_UID + 33
+        });
+        final long[] freqs = {1, 12, 123, 12, 1234};
+        final long[][] uidTimesMs = {
+                {4, 10, 5, 9, 4},
+                {5, 1, 12, 2, 10},
+                {8, 25, 3, 0, 42}
+        };
+        doAnswer(invocation -> {
+            final KernelUidCpuFreqTimeReader.Callback callback =
+                    (KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
+            callback.onCpuFreqs(freqs);
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]);
+            }
+            return null;
+        }).when(mKernelUidCpuFreqTimeReader).readDelta(
+                any(KernelUidCpuFreqTimeReader.Callback.class));
+
+        // RUN
+        mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked();
+
+        // VERIFY
+        assertArrayEquals("Unexpected cpu freqs", freqs, mBatteryStatsImpl.getCpuFreqs());
+        for (int i = 0; i < testUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            if (UserHandle.isIsolated(testUids[i])) {
+                assertNull("There shouldn't be an entry for isolated uid=" + testUids[i], u);
+                continue;
+            }
+            assertNotNull("No entry for uid=" + testUids[i], u);
+
+            assertArrayEquals("Unexpected cpu times for uid=" + testUids[i],
+                    uidTimesMs[i], u.getCpuFreqTimes(STATS_SINCE_CHARGED));
+            assertNull("Unexpected screen-off cpu times for uid=" + testUids[i],
+                    u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED));
+        }
+        verify(mKernelUidCpuFreqTimeReader).removeUid(isolatedUid);
+
+
+        // Add an isolated uid mapping and repeat the test.
+
+        // PRECONDITIONS
+        final int ownerUid = UserHandle.getUid(testUserId, FIRST_APPLICATION_UID + 42);
+        mBatteryStatsImpl.addIsolatedUidLocked(isolatedUid, ownerUid);
+        final long[][] deltasMs = {
+                {3, 12, 55, 100, 32},
+                {32483274, 232349349, 123, 2398, 0},
+                {43, 3345, 2143, 123, 4554}
+        };
+        doAnswer(invocation -> {
+            final KernelUidCpuFreqTimeReader.Callback callback =
+                    (KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
+            callback.onCpuFreqs(freqs);
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuFreqTime(testUids[i], deltasMs[i]);
+            }
+            return null;
+        }).when(mKernelUidCpuFreqTimeReader).readDelta(
+                any(KernelUidCpuFreqTimeReader.Callback.class));
+
+        // RUN
+        mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked();
+
+        // VERIFY
+        assertArrayEquals("Unexpected cpu freqs", freqs, mBatteryStatsImpl.getCpuFreqs());
+        for (int i = 0; i < testUids.length; ++i) {
+            BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            final long[] expectedTimes;
+            if (UserHandle.isIsolated(testUids[i])) {
+                assertNull("There shouldn't be an entry for isolated uid=" + testUids[i], u);
+                // Since we added a mapping, an entry should've been created for owner uid.
+                u = mBatteryStatsImpl.getUidStats().get(ownerUid);
+                expectedTimes = deltasMs[i];
+                assertNotNull("No entry for owner uid=" + ownerUid, u);
+            } else {
+                assertNotNull("No entry for uid=" + testUids[i], u);
+                expectedTimes = sum(uidTimesMs[i], deltasMs[i]);
+            }
+
+            assertArrayEquals("Unexpected cpu times for uid=" + testUids[i],
+                    expectedTimes, u.getCpuFreqTimes(STATS_SINCE_CHARGED));
+            assertNull("Unexpected screen-off cpu times for uid=" + testUids[i],
+                    u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED));
+        }
+    }
+
+    @Test
+    public void testReadKernelUiidCpuFreqTimesLocked_invalidUid() {
+        // PRECONDITIONS
+        mBatteryStatsImpl.updateTimeBasesLocked(true, false, 0, 0);
+
+        final int testUserId = 11;
+        final int invalidUserId = 15;
+        final int invalidUid = UserHandle.getUid(invalidUserId, FIRST_APPLICATION_UID + 99);
+        when(mUserInfoProvider.exists(testUserId)).thenReturn(true);
+        when(mUserInfoProvider.exists(invalidUserId)).thenReturn(false);
+        final int[] testUids = getUids(testUserId, new int[] {
+                FIRST_APPLICATION_UID + 22,
+                FIRST_APPLICATION_UID + 27,
+                FIRST_APPLICATION_UID + 33
+        });
+        final long[] freqs = {1, 12, 123, 12, 1234};
+        final long[][] uidTimesMs = {
+                {4, 10, 5, 9, 4},
+                {5, 1, 12, 2, 10},
+                {8, 25, 3, 0, 42}
+        };
+        doAnswer(invocation -> {
+            final KernelUidCpuFreqTimeReader.Callback callback =
+                    (KernelUidCpuFreqTimeReader.Callback) invocation.getArguments()[0];
+            callback.onCpuFreqs(freqs);
+            for (int i = 0; i < testUids.length; ++i) {
+                callback.onUidCpuFreqTime(testUids[i], uidTimesMs[i]);
+            }
+            // And one for the invalid uid
+            callback.onUidCpuFreqTime(invalidUid, new long[] {12, 839, 32, 34, 21});
+            return null;
+        }).when(mKernelUidCpuFreqTimeReader).readDelta(
+                any(KernelUidCpuFreqTimeReader.Callback.class));
+
+        // RUN
+        mBatteryStatsImpl.readKernelUidCpuFreqTimesLocked();
+
+        // VERIFY
+        assertArrayEquals("Unexpected cpu freqs", freqs, mBatteryStatsImpl.getCpuFreqs());
+        for (int i = 0; i < testUids.length; ++i) {
+            final BatteryStats.Uid u = mBatteryStatsImpl.getUidStats().get(testUids[i]);
+            assertNotNull("No entry for uid=" + testUids[i], u);
+
+            assertArrayEquals("Unexpected cpu times for uid=" + testUids[i],
+                    uidTimesMs[i], u.getCpuFreqTimes(STATS_SINCE_CHARGED));
+            assertNull("Unexpected screen-off cpu times for uid=" + testUids[i],
+                    u.getScreenOffCpuFreqTimes(STATS_SINCE_CHARGED));
+        }
+        assertNull("There shouldn't be an entry for invalid uid=" + invalidUid,
+                mBatteryStatsImpl.getUidStats().get(invalidUid));
+        verify(mKernelUidCpuFreqTimeReader).removeUid(invalidUid);
+    }
+
+    private void initKernelCpuSpeedReaders(int count) {
+        mKernelCpuSpeedReaders = new KernelCpuSpeedReader[count];
+        for (int i = 0; i < count; ++i) {
+            mKernelCpuSpeedReaders[i] = Mockito.mock(KernelCpuSpeedReader.class);
+        }
+        mBatteryStatsImpl.setKernelCpuSpeedReaders(mKernelCpuSpeedReaders);
+    }
+
+    private ArrayList<BatteryStatsImpl.StopwatchTimer> getPartialTimers(int... uids) {
+        final ArrayList<BatteryStatsImpl.StopwatchTimer> partialTimers = new ArrayList<>();
+        final BatteryStatsImpl.TimeBase timeBase = new BatteryStatsImpl.TimeBase();
+        for (int uid : uids) {
+            final BatteryStatsImpl.Uid u = mBatteryStatsImpl.getUidStatsLocked(uid);
+            final BatteryStatsImpl.StopwatchTimer timer = new BatteryStatsImpl.StopwatchTimer(
+                    mClocks, u, WAKE_TYPE_PARTIAL, null, timeBase);
+            partialTimers.add(timer);
+        }
+        return partialTimers;
+    }
+
+    private long[] sum(long[] a, long[] b) {
+        assertEquals("Arrays a: " + Arrays.toString(a) + ", b: " + Arrays.toString(b),
+                a.length, b.length);
+        final long[] result = new long[a.length];
+        for (int i = 0; i < a.length; ++i) {
+            result[i] = a[i] + b[i];
+        }
+        return result;
+    }
+
+    private int[] getUids(int userId, int[] appIds) {
+        final int[] uids = new int[appIds.length];
+        for (int i = appIds.length - 1; i >= 0; --i) {
+            uids[i] = UserHandle.getUid(userId, appIds[i]);
+        }
+        return uids;
+    }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
index 8fd4e31..0294095 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsSensorTest.java
@@ -488,7 +488,7 @@
         bi.noteStartSensorLocked(UID, SENSOR_ID);
 
         clocks.realtime += 111;
-        clocks.uptime += 1111;
+        clocks.uptime += 111;
 
         // Timebase and timer was on so times have increased.
         assertEquals(111_000, timer.getTotalTimeLocked(1000*clocks.realtime, which));
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index f171afc..c1ca864 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -20,6 +20,7 @@
 
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
+        BatteryStatsCpuTimesTest.class,
         BatteryStatsBackgroundStatsTest.class,
         BatteryStatsCounterTest.class,
         BatteryStatsDualTimerTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index a123fce..73ee3e5 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.os;
 
+import java.util.ArrayList;
+
 /**
  * Mocks a BatteryStatsImpl object.
  */
@@ -48,5 +50,45 @@
     public TimeBase getOnBatteryScreenOffBackgroundTimeBase(int uid) {
         return getUidStatsLocked(uid).mOnBatteryScreenOffBackgroundTimeBase;
     }
+
+    public MockBatteryStatsImpl setPowerProfile(PowerProfile powerProfile) {
+        mPowerProfile = powerProfile;
+        return this;
+    }
+
+    public MockBatteryStatsImpl setKernelUidCpuFreqTimeReader(KernelUidCpuFreqTimeReader reader) {
+        mKernelUidCpuFreqTimeReader = reader;
+        return this;
+    }
+
+    public MockBatteryStatsImpl setKernelUidCpuTimeReader(KernelUidCpuTimeReader reader) {
+        mKernelUidCpuTimeReader = reader;
+        return this;
+    }
+
+    public MockBatteryStatsImpl setKernelCpuSpeedReaders(KernelCpuSpeedReader[] readers) {
+        mKernelCpuSpeedReaders = readers;
+        return this;
+    }
+
+    public MockBatteryStatsImpl setUserInfoProvider(UserInfoProvider provider) {
+        mUserInfoProvider = provider;
+        return this;
+    }
+
+    public MockBatteryStatsImpl setPartialTimers(ArrayList<StopwatchTimer> partialTimers) {
+        mPartialTimers = partialTimers;
+        return this;
+    }
+
+    public MockBatteryStatsImpl setLastPartialTimers(ArrayList<StopwatchTimer> lastPartialTimers) {
+        mLastPartialTimers = lastPartialTimers;
+        return this;
+    }
+
+    public MockBatteryStatsImpl setOnBatteryInternal(boolean onBatteryInternal) {
+        mOnBatteryInternal = onBatteryInternal;
+        return this;
+    }
 }
 
diff --git a/data/etc/framework-sysconfig.xml b/data/etc/framework-sysconfig.xml
index 7fafef7..3a81c13 100644
--- a/data/etc/framework-sysconfig.xml
+++ b/data/etc/framework-sysconfig.xml
@@ -28,4 +28,6 @@
     <backup-transport-whitelisted-service
         service="android/com.android.internal.backup.LocalTransportService" />
 
+    <!-- Whitelist of bundled applications which all handle URLs to their websites by default -->
+    <app-link package="com.android.carrierdefaultapp" />
 </config>
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index 2a10bdd..b3bba07 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -15,7 +15,7 @@
 #
 # Generic key layout file for full alphabetic US English PC style external keyboards.
 #
-# This file is intentionally very generic and is intended to support a broad rang of keyboards.
+# This file is intentionally very generic and is intended to support a broad range of keyboards.
 # Do not edit the generic key layout to support a specific keyboard; instead, create
 # a new key layout file with the required keyboard configuration.
 #
diff --git a/data/keyboards/Generic_Iot.kl b/data/keyboards/Generic_Iot.kl
new file mode 100644
index 0000000..89df224
--- /dev/null
+++ b/data/keyboards/Generic_Iot.kl
@@ -0,0 +1,443 @@
+# Copyright (C) 2017 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.
+
+#
+# Generic key layout file for full alphabetic US English PC style external keyboards.
+#
+# This file is intentionally very generic and is intended to support a broad range of keyboards.
+# Do not edit the generic key layout to support a specific keyboard; instead, create
+# a new key layout file with the required keyboard configuration.
+#
+
+key 1     ESCAPE
+key 2     1
+key 3     2
+key 4     3
+key 5     4
+key 6     5
+key 7     6
+key 8     7
+key 9     8
+key 10    9
+key 11    0
+key 12    MINUS
+key 13    EQUALS
+key 14    DEL
+key 15    TAB
+key 16    Q
+key 17    W
+key 18    E
+key 19    R
+key 20    T
+key 21    Y
+key 22    U
+key 23    I
+key 24    O
+key 25    P
+key 26    LEFT_BRACKET
+key 27    RIGHT_BRACKET
+key 28    ENTER
+key 29    CTRL_LEFT
+key 30    A
+key 31    S
+key 32    D
+key 33    F
+key 34    G
+key 35    H
+key 36    J
+key 37    K
+key 38    L
+key 39    SEMICOLON
+key 40    APOSTROPHE
+key 41    GRAVE
+key 42    SHIFT_LEFT
+key 43    BACKSLASH
+key 44    Z
+key 45    X
+key 46    C
+key 47    V
+key 48    B
+key 49    N
+key 50    M
+key 51    COMMA
+key 52    PERIOD
+key 53    SLASH
+key 54    SHIFT_RIGHT
+key 55    NUMPAD_MULTIPLY
+key 56    ALT_LEFT
+key 57    SPACE
+key 58    CAPS_LOCK
+key 59    F1
+key 60    F2
+key 61    F3
+key 62    F4
+key 63    F5
+key 64    F6
+key 65    F7
+key 66    F8
+key 67    F9
+key 68    F10
+key 69    NUM_LOCK
+key 70    SCROLL_LOCK
+key 71    NUMPAD_7
+key 72    NUMPAD_8
+key 73    NUMPAD_9
+key 74    NUMPAD_SUBTRACT
+key 75    NUMPAD_4
+key 76    NUMPAD_5
+key 77    NUMPAD_6
+key 78    NUMPAD_ADD
+key 79    NUMPAD_1
+key 80    NUMPAD_2
+key 81    NUMPAD_3
+key 82    NUMPAD_0
+key 83    NUMPAD_DOT
+# key 84 (undefined)
+key 85    ZENKAKU_HANKAKU
+key 86    BACKSLASH
+key 87    F11
+key 88    F12
+key 89    RO
+# key 90 "KEY_KATAKANA"
+# key 91 "KEY_HIRAGANA"
+key 92    HENKAN
+key 93    KATAKANA_HIRAGANA
+key 94    MUHENKAN
+key 95    NUMPAD_COMMA
+key 96    NUMPAD_ENTER
+key 97    CTRL_RIGHT
+key 98    NUMPAD_DIVIDE
+key 99    SYSRQ
+key 100   ALT_RIGHT
+# key 101 "KEY_LINEFEED"
+key 102   MOVE_HOME
+key 103   DPAD_UP
+key 104   PAGE_UP
+key 105   DPAD_LEFT
+key 106   DPAD_RIGHT
+key 107   MOVE_END
+key 108   DPAD_DOWN
+key 109   PAGE_DOWN
+key 110   INSERT
+key 111   FORWARD_DEL
+# key 112 "KEY_MACRO"
+key 113   VOLUME_MUTE
+key 114   VOLUME_DOWN
+key 115   VOLUME_UP
+key 116   POWER
+key 117   NUMPAD_EQUALS
+# key 118 "KEY_KPPLUSMINUS"
+key 119   BREAK
+# key 120 (undefined)
+key 121   NUMPAD_COMMA
+key 122   KANA
+key 123   EISU
+key 124   YEN
+key 125   META_LEFT
+key 126   META_RIGHT
+key 127   MENU
+key 128   MEDIA_STOP
+# key 129 "KEY_AGAIN"
+# key 130 "KEY_PROPS"
+# key 131 "KEY_UNDO"
+# key 132 "KEY_FRONT"
+key 133   COPY
+# key 134 "KEY_OPEN"
+key 135   PASTE
+# key 136 "KEY_FIND"
+key 137   CUT
+# key 138 "KEY_HELP"
+key 139   MENU
+key 140   CALCULATOR
+# key 141 "KEY_SETUP"
+key 142   SLEEP
+key 143   WAKEUP
+# key 144 "KEY_FILE"
+# key 145 "KEY_SENDFILE"
+# key 146 "KEY_DELETEFILE"
+# key 147 "KEY_XFER"
+# key 148 "KEY_PROG1"
+# key 149 "KEY_PROG2"
+key 150   EXPLORER
+# key 151 "KEY_MSDOS"
+key 152   POWER
+# key 153 "KEY_DIRECTION"
+# key 154 "KEY_CYCLEWINDOWS"
+key 155   ENVELOPE
+key 156   BOOKMARK
+# key 157 "KEY_COMPUTER"
+key 158   BACK
+key 159   FORWARD
+key 160   MEDIA_CLOSE
+key 161   MEDIA_EJECT
+key 162   MEDIA_EJECT
+key 163   MEDIA_NEXT
+key 164   MEDIA_PLAY_PAUSE
+key 165   MEDIA_PREVIOUS
+key 166   MEDIA_STOP
+key 167   MEDIA_RECORD
+key 168   MEDIA_REWIND
+key 169   CALL
+# key 170 "KEY_ISO"
+key 171   MUSIC
+key 172   HOME
+# key 173 "KEY_REFRESH"
+# key 174 "KEY_EXIT"
+# key 175 "KEY_MOVE"
+# key 176 "KEY_EDIT"
+key 177   PAGE_UP
+key 178   PAGE_DOWN
+key 179   NUMPAD_LEFT_PAREN
+key 180   NUMPAD_RIGHT_PAREN
+# key 181 "KEY_NEW"
+# key 182 "KEY_REDO"
+# key 183   F13
+# key 184   F14
+# key 185   F15
+# key 186   F16
+# key 187   F17
+# key 188   F18
+# key 189   F19
+# key 190   F20
+# key 191   F21
+# key 192   F22
+# key 193   F23
+# key 194   F24
+# key 195 (undefined)
+# key 196 (undefined)
+# key 197 (undefined)
+# key 198 (undefined)
+# key 199 (undefined)
+key 200   MEDIA_PLAY
+key 201   MEDIA_PAUSE
+# key 202 "KEY_PROG3"
+# key 203 "KEY_PROG4"
+# key 204 (undefined)
+# key 205 "KEY_SUSPEND"
+# key 206 "KEY_CLOSE"
+key 207   MEDIA_PLAY
+key 208   MEDIA_FAST_FORWARD
+# key 209 "KEY_BASSBOOST"
+# key 210 "KEY_PRINT"
+# key 211 "KEY_HP"
+key 212   CAMERA
+key 213   MUSIC
+# key 214 "KEY_QUESTION"
+key 215   ENVELOPE
+# key 216 "KEY_CHAT"
+key 217   SEARCH
+# key 218 "KEY_CONNECT"
+# key 219 "KEY_FINANCE"
+# key 220 "KEY_SPORT"
+# key 221 "KEY_SHOP"
+# key 222 "KEY_ALTERASE"
+# key 223 "KEY_CANCEL"
+key 224   BRIGHTNESS_DOWN
+key 225   BRIGHTNESS_UP
+key 226   HEADSETHOOK
+key 227   POUND
+key 228   STAR
+
+key 256   BUTTON_1
+key 257   BUTTON_2
+key 258   BUTTON_3
+key 259   BUTTON_4
+key 260   BUTTON_5
+key 261   BUTTON_6
+key 262   BUTTON_7
+key 263   BUTTON_8
+key 264   BUTTON_9
+key 265   BUTTON_10
+key 266   BUTTON_11
+key 267   BUTTON_12
+key 268   BUTTON_13
+key 269   BUTTON_14
+key 270   BUTTON_15
+key 271   BUTTON_16
+
+key 288   BUTTON_1
+key 289   BUTTON_2
+key 290   BUTTON_3
+key 291   BUTTON_4
+key 292   BUTTON_5
+key 293   BUTTON_6
+key 294   BUTTON_7
+key 295   BUTTON_8
+key 296   BUTTON_9
+key 297   BUTTON_10
+key 298   BUTTON_11
+key 299   BUTTON_12
+key 300   BUTTON_13
+key 301   BUTTON_14
+key 302   BUTTON_15
+key 303   BUTTON_16
+
+
+key 304   BUTTON_A
+key 305   BUTTON_B
+key 306   BUTTON_C
+key 307   BUTTON_X
+key 308   BUTTON_Y
+key 309   BUTTON_Z
+key 310   BUTTON_L1
+key 311   BUTTON_R1
+key 312   BUTTON_L2
+key 313   BUTTON_R2
+key 314   BUTTON_SELECT
+key 315   BUTTON_START
+key 316   BUTTON_MODE
+key 317   BUTTON_THUMBL
+key 318   BUTTON_THUMBR
+
+
+# key 352 "KEY_OK"
+key 353   DPAD_CENTER
+# key 354 "KEY_GOTO"
+# key 355 "KEY_CLEAR"
+# key 356 "KEY_POWER2"
+# key 357 "KEY_OPTION"
+# key 358 "KEY_INFO"
+# key 359 "KEY_TIME"
+# key 360 "KEY_VENDOR"
+# key 361 "KEY_ARCHIVE"
+key 362   GUIDE
+# key 363 "KEY_CHANNEL"
+# key 364 "KEY_FAVORITES"
+# key 365 "KEY_EPG"
+key 366   DVR
+# key 367 "KEY_MHP"
+# key 368 "KEY_LANGUAGE"
+# key 369 "KEY_TITLE"
+# key 370 "KEY_SUBTITLE"
+# key 371 "KEY_ANGLE"
+# key 372 "KEY_ZOOM"
+# key 373 "KEY_MODE"
+# key 374 "KEY_KEYBOARD"
+# key 375 "KEY_SCREEN"
+# key 376 "KEY_PC"
+key 377   TV
+# key 378 "KEY_TV2"
+# key 379 "KEY_VCR"
+# key 380 "KEY_VCR2"
+# key 381 "KEY_SAT"
+# key 382 "KEY_SAT2"
+# key 383 "KEY_CD"
+# key 384 "KEY_TAPE"
+# key 385 "KEY_RADIO"
+# key 386 "KEY_TUNER"
+# key 387 "KEY_PLAYER"
+# key 388 "KEY_TEXT"
+# key 389 "KEY_DVD"
+# key 390 "KEY_AUX"
+# key 391 "KEY_MP3"
+# key 392 "KEY_AUDIO"
+# key 393 "KEY_VIDEO"
+# key 394 "KEY_DIRECTORY"
+# key 395 "KEY_LIST"
+# key 396 "KEY_MEMO"
+key 397   CALENDAR
+# key 398 "KEY_RED"
+# key 399 "KEY_GREEN"
+# key 400 "KEY_YELLOW"
+# key 401 "KEY_BLUE"
+key 402   CHANNEL_UP
+key 403   CHANNEL_DOWN
+# key 404 "KEY_FIRST"
+# key 405 "KEY_LAST"
+# key 406 "KEY_AB"
+# key 407 "KEY_NEXT"
+# key 408 "KEY_RESTART"
+# key 409 "KEY_SLOW"
+# key 410 "KEY_SHUFFLE"
+# key 411 "KEY_BREAK"
+# key 412 "KEY_PREVIOUS"
+# key 413 "KEY_DIGITS"
+# key 414 "KEY_TEEN"
+# key 415 "KEY_TWEN"
+
+key 429   CONTACTS
+
+# key 448 "KEY_DEL_EOL"
+# key 449 "KEY_DEL_EOS"
+# key 450 "KEY_INS_LINE"
+# key 451 "KEY_DEL_LINE"
+
+
+key 464   FUNCTION
+key 465   ESCAPE            FUNCTION
+key 466   F1                FUNCTION
+key 467   F2                FUNCTION
+key 468   F3                FUNCTION
+key 469   F4                FUNCTION
+key 470   F5                FUNCTION
+key 471   F6                FUNCTION
+key 472   F7                FUNCTION
+key 473   F8                FUNCTION
+key 474   F9                FUNCTION
+key 475   F10               FUNCTION
+key 476   F11               FUNCTION
+key 477   F12               FUNCTION
+key 478   1                 FUNCTION
+key 479   2                 FUNCTION
+key 480   D                 FUNCTION
+key 481   E                 FUNCTION
+key 482   F                 FUNCTION
+key 483   S                 FUNCTION
+key 484   B                 FUNCTION
+
+
+# key 497 KEY_BRL_DOT1
+# key 498 KEY_BRL_DOT2
+# key 499 KEY_BRL_DOT3
+# key 500 KEY_BRL_DOT4
+# key 501 KEY_BRL_DOT5
+# key 502 KEY_BRL_DOT6
+# key 503 KEY_BRL_DOT7
+# key 504 KEY_BRL_DOT8
+
+key 580   APP_SWITCH
+key 582   VOICE_ASSIST
+
+# Keys defined by HID usages
+key usage 0x0c006F BRIGHTNESS_UP
+key usage 0x0c0070 BRIGHTNESS_DOWN
+
+# Joystick and game controller axes.
+# Axes that are not mapped will be assigned generic axis numbers by the input subsystem.
+axis 0x00 X
+axis 0x01 Y
+axis 0x02 Z
+axis 0x03 RX
+axis 0x04 RY
+axis 0x05 RZ
+axis 0x06 THROTTLE
+axis 0x07 RUDDER
+axis 0x08 WHEEL
+axis 0x09 GAS
+axis 0x0a BRAKE
+axis 0x10 HAT_X
+axis 0x11 HAT_Y
+
+# LEDs
+led 0x00 NUM_LOCK
+led 0x01 CAPS_LOCK
+led 0x02 SCROLL_LOCK
+led 0x03 COMPOSE
+led 0x04 KANA
+led 0x05 SLEEP
+led 0x06 SUSPEND
+led 0x07 MUTE
+led 0x08 MISC
+led 0x09 MAIL
+led 0x0a CHARGING
diff --git a/dirty-image-objects b/dirty-image-objects
new file mode 100644
index 0000000..9b4d199
--- /dev/null
+++ b/dirty-image-objects
@@ -0,0 +1,176 @@
+#
+# Copyright (C) 2017 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.
+#
+#
+#
+# Dirty-image-objects file for boot image.
+#
+# Objects in this file are known dirty at runtime. Current this includes:
+#   - classes with known dirty static fields.
+#
+# The image writer will bin these objects together in the image.
+#
+# This file can be generated using imgdiag with a command such as:
+#   adb shell imgdiag --image-diff-pid=<app pid> --zygote-diff-pid=<zygote pid> \
+#     --boot-image=/system/framework/boot.art --dump-dirty-objects
+# Then, grep for lines containing "Private dirty object" from the output.
+# This particular file was generated by dumping systemserver and systemui.
+#
+java.lang.System
+java.net.Inet4Address
+java.lang.Thread
+java.lang.Throwable
+java.util.Collections
+javax.net.ssl.SSLContext
+java.nio.charset.Charset
+java.security.Provider
+javax.net.ssl.HttpsURLConnection
+javax.net.ssl.SSLSocketFactory
+java.util.TimeZone
+java.util.Locale
+java.util.function.ToIntFunction
+sun.misc.FormattedFloatingDecimal
+java.util.stream.IntStream
+android.icu.util.TimeZone
+libcore.io.DropBox
+org.apache.harmony.luni.internal.util.TimezoneGetter
+dalvik.system.SocketTagger
+dalvik.system.CloseGuard
+java.lang.ref.FinalizerReference
+com.android.org.conscrypt.ct.CTLogStoreImpl
+com.android.org.conscrypt.SSLParametersImpl
+com.android.org.conscrypt.OpenSSLContextImpl
+com.android.org.conscrypt.SSLParametersImpl$AliasChooser
+com.android.org.conscrypt.SSLParametersImpl$PSKCallbacks
+com.android.org.conscrypt.NativeCrypto$SSLHandshakeCallbacks
+com.android.okhttp.OkHttpClient
+com.android.okhttp.okio.SegmentPool
+com.android.okhttp.okio.AsyncTimeout
+com.android.okhttp.HttpUrl
+android.os.StrictMode
+com.android.internal.os.BinderInternal
+android.os.storage.StorageManager
+android.os.Trace
+android.app.ActivityManager
+android.media.MediaRouter
+android.os.Environment
+android.view.ThreadedRenderer
+android.media.AudioManager
+android.app.AlarmManager
+android.telephony.TelephonyManager
+android.bluetooth.BluetoothAdapter
+com.android.internal.os.SomeArgs
+android.os.LocaleList
+android.view.WindowManagerGlobal
+android.media.AudioSystem
+android.ddm.DdmHandleAppName
+android.provider.Settings
+android.view.ViewRootImpl
+android.net.ConnectivityManager
+android.app.ActivityThread
+android.os.BaseBundle
+android.util.ArraySet
+android.view.View
+android.os.ServiceManager
+android.view.ViewTreeObserver
+android.hardware.input.InputManager
+android.os.UEventObserver
+android.app.NotificationManager
+android.hardware.display.DisplayManagerGlobal
+android.os.Binder
+android.app.AppOpsManager
+android.content.ContentResolver
+android.app.backup.BackupManager
+android.util.ArrayMap
+android.os.Looper
+android.graphics.Bitmap
+android.view.textservice.TextServicesManager
+com.android.internal.inputmethod.InputMethodUtils
+android.app.QueuedWork
+android.graphics.TemporaryBuffer
+android.widget.ImageView
+android.database.sqlite.SQLiteGlobal
+android.view.autofill.Helper
+android.text.method.SingleLineTransformationMethod
+com.android.internal.os.RuntimeInit
+android.view.inputmethod.InputMethodManager
+android.hardware.SystemSensorManager
+android.database.CursorWindow
+android.text.TextUtils
+android.media.PlayerBase
+android.app.ResourcesManager
+android.os.Message
+android.view.accessibility.AccessibilityManager
+android.app.Notification
+android.provider.ContactsContract$ContactNameColumns
+android.provider.CalendarContract$EventsColumns
+android.provider.CalendarContract$CalendarColumns
+android.provider.CalendarContract$SyncColumns
+android.provider.ContactsContract$ContactsColumns
+android.content.pm.PackageManager$OnPermissionsChangedListener
+android.net.IpConfiguration$ProxySettings
+android.provider.ContactsContract$ContactOptionsColumns
+android.net.wifi.SupplicantState
+android.provider.ContactsContract$ContactStatusColumns
+android.view.accessibility.AccessibilityManager$TouchExplorationStateChangeListener
+android.provider.CalendarContract$CalendarSyncColumns
+android.bluetooth.BluetoothProfile$ServiceListener
+android.provider.ContactsContract$ContactCounts
+android.net.IpConfiguration$IpAssignment
+android.text.TextWatcher
+android.graphics.Bitmap$CompressFormat
+android.location.LocationListener
+sun.security.jca.Providers
+java.lang.CharSequence
+android.icu.util.ULocale
+dalvik.system.BaseDexClassLoader
+android.icu.text.BreakIterator
+libcore.io.EventLogger
+libcore.net.NetworkSecurityPolicy
+android.icu.text.UnicodeSet
+com.android.org.conscrypt.TrustedCertificateStore$PreloadHolder
+android.app.SearchManager
+android.os.Build
+android.app.ContextImpl
+android.app.WallpaperManager
+android.security.net.config.ApplicationConfig
+android.animation.LayoutTransition
+android.widget.TextView
+com.android.internal.logging.MetricsLogger
+android.renderscript.RenderScriptCacheDir
+android.os.Process
+android.os.Handler
+android.content.Context
+android.graphics.drawable.AdaptiveIconDrawable
+android.provider.FontsContract
+android.text.style.SuggestionSpan
+android.graphics.drawable.VectorDrawable$VGroup
+android.view.ViewStub
+android.text.style.MetricAffectingSpan
+android.content.SharedPreferences$OnSharedPreferenceChangeListener
+android.app.PendingIntent
+android.text.SpanWatcher
+android.widget.FrameLayout
+android.net.NetworkRequest$Type
+android.net.NetworkInfo$State
+android.graphics.drawable.GradientDrawable
+android.text.style.AlignmentSpan
+android.widget.LinearLayout
+android.text.style.CharacterStyle
+android.view.View$OnApplyWindowInsetsListener
+android.view.MenuItem
+android.text.style.ReplacementSpan
+android.graphics.drawable.Icon
+android.widget.Button
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 1d8b583..3a8dfb0 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -548,29 +548,7 @@
             final int weight = (mWeight == RESOLVE_BY_FONT_TABLE) ? base.mWeight : mWeight;
             final boolean italic =
                     (mItalic == RESOLVE_BY_FONT_TABLE) ? (base.mStyle & ITALIC) != 0 : mItalic == 1;
-            final int key = weight << 1 | (italic ? 1 : 0);
-
-            Typeface typeface;
-            synchronized(sLock) {
-                SparseArray<Typeface> innerCache = sTypefaceCache.get(base.native_instance);
-                if (innerCache != null) {
-                    typeface = innerCache.get(key);
-                    if (typeface != null) {
-                        return typeface;
-                    }
-                }
-
-                typeface = new Typeface(
-                        nativeCreateFromTypefaceWithExactStyle(
-                                base.native_instance, weight, italic));
-
-                if (innerCache == null) {
-                    innerCache = new SparseArray<>(4); // [regular, bold] x [upright, italic]
-                    sTypefaceCache.put(base.native_instance, innerCache);
-                }
-                innerCache.put(key, typeface);
-            }
-            return typeface;
+            return createWeightStyle(base, weight, italic);
         }
 
         /**
@@ -688,7 +666,8 @@
      * style from the same family of an existing typeface object. If family is
      * null, this selects from the default font's family.
      *
-     * @param family May be null. The name of the existing type face.
+     * @param family An existing {@link Typeface} object. In case of {@code null}, the default
+     *               typeface is used instead.
      * @param style  The style (normal, bold, italic) of the typeface.
      *               e.g. NORMAL, BOLD, ITALIC, BOLD_ITALIC
      * @return The best matching typeface.
@@ -727,6 +706,53 @@
         return typeface;
     }
 
+    /**
+     * Creates a typeface object that best matches the specified existing typeface and the specified
+     * weight and italic style
+     *
+     * @param family An existing {@link Typeface} object. In case of {@code null}, the default
+     *               typeface is used instead.
+     * @param weight The desired weight to be drawn.
+     * @param italic {@code true} if italic style is desired to be drawn. Otherwise, {@code false}
+     * @return A {@link Typeface} object for drawing specified weight and italic style. Never
+     *         returns {@code null}
+     */
+    public static @NonNull Typeface create(@Nullable Typeface family,
+            @IntRange(from = 1, to = 1000) int weight, boolean italic) {
+        Preconditions.checkArgumentInRange(weight, 0, 1000, "weight");
+        if (family == null) {
+            family = sDefaultTypeface;
+        }
+        return createWeightStyle(family, weight, italic);
+    }
+
+    private static @NonNull Typeface createWeightStyle(@NonNull Typeface base,
+            @IntRange(from = 1, to = 1000) int weight, boolean italic) {
+        final int key = weight << 1 | (italic ? 1 : 0);
+
+        Typeface typeface;
+        synchronized(sLock) {
+            SparseArray<Typeface> innerCache = sTypefaceCache.get(base.native_instance);
+            if (innerCache != null) {
+                typeface = innerCache.get(key);
+                if (typeface != null) {
+                    return typeface;
+                }
+            }
+
+            typeface = new Typeface(
+                    nativeCreateFromTypefaceWithExactStyle(
+                            base.native_instance, weight, italic));
+
+            if (innerCache == null) {
+                innerCache = new SparseArray<>(4); // [regular, bold] x [upright, italic]
+                sTypefaceCache.put(base.native_instance, innerCache);
+            }
+            innerCache.put(key, typeface);
+        }
+        return typeface;
+    }
+
     /** @hide */
     public static Typeface createFromTypefaceWithVariation(@Nullable Typeface family,
             @NonNull List<FontVariationAxis> axes) {
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index acc7539..4c84ad2 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -176,7 +176,7 @@
         return sRenderPipelineType;
     }
     char prop[PROPERTY_VALUE_MAX];
-    property_get(PROPERTY_RENDERER, prop, "opengl");
+    property_get(PROPERTY_RENDERER, prop, "skiagl");
     if (!strcmp(prop, "skiagl") ) {
         ALOGD("Skia GL Pipeline");
         sRenderPipelineType = RenderPipelineType::SkiaGL;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index 55eeb7f..61b3876 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -241,7 +241,8 @@
     if (CC_LIKELY(layerType != LayerType::RenderLayer)
             || CC_UNLIKELY(!isRenderable())
             || CC_UNLIKELY(properties().getWidth() == 0)
-            || CC_UNLIKELY(properties().getHeight() == 0)) {
+            || CC_UNLIKELY(properties().getHeight() == 0)
+            || CC_UNLIKELY(!properties().fitsOnLayer())) {
         if (CC_UNLIKELY(hasLayer())) {
             renderthread::CanvasContext::destroyLayer(this);
         }
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 9683d06..d191b56 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -25,7 +25,6 @@
 
 #include <SkCanvasStateUtils.h>
 #include <SkColorFilter.h>
-// TODO remove me!
 #include <SkColorSpaceXformCanvas.h>
 #include <SkDrawable.h>
 #include <SkDeque.h>
@@ -48,25 +47,33 @@
     return new SkiaCanvas(bitmap);
 }
 
-Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas, XformToSRGB xformToSRGB) {
-    return new SkiaCanvas(skiaCanvas, xformToSRGB);
+Canvas* Canvas::create_canvas(SkCanvas* skiaCanvas) {
+    return new SkiaCanvas(skiaCanvas);
 }
 
 SkiaCanvas::SkiaCanvas() {}
 
-SkiaCanvas::SkiaCanvas(SkCanvas* canvas, XformToSRGB xformToSRGB)
-    : mCanvas(canvas)
-{
-    LOG_ALWAYS_FATAL_IF(XformToSRGB::kImmediate == xformToSRGB);
-}
+SkiaCanvas::SkiaCanvas(SkCanvas* canvas) : mCanvas(canvas) {}
 
 SkiaCanvas::SkiaCanvas(const SkBitmap& bitmap) {
     sk_sp<SkColorSpace> cs = bitmap.refColorSpace();
     mCanvasOwned =
             std::unique_ptr<SkCanvas>(new SkCanvas(bitmap, SkCanvas::ColorBehavior::kLegacy));
-    mCanvasWrapper = SkCreateColorSpaceXformCanvas(mCanvasOwned.get(),
-            cs == nullptr ? SkColorSpace::MakeSRGB() : std::move(cs));
-    mCanvas = mCanvasWrapper.get();
+    if (cs.get() == nullptr || cs->isSRGB()) {
+        if(!uirenderer::Properties::isSkiaEnabled()) {
+            mCanvasWrapper = SkCreateColorSpaceXformCanvas(mCanvasOwned.get(), SkColorSpace::MakeSRGB());
+            mCanvas = mCanvasWrapper.get();
+        } else {
+            mCanvas = mCanvasOwned.get();
+        }
+    } else {
+        /** The wrapper is needed if we are drawing into a non-sRGB destination, since
+         *  we need to transform all colors (not just bitmaps via filters) into the
+         *  destination's colorspace.
+         */
+        mCanvasWrapper = SkCreateColorSpaceXformCanvas(mCanvasOwned.get(), std::move(cs));
+        mCanvas = mCanvasWrapper.get();
+    }
 }
 
 SkiaCanvas::~SkiaCanvas() {}
@@ -75,6 +82,7 @@
     if (mCanvas != skiaCanvas) {
         mCanvas = skiaCanvas;
         mCanvasOwned.reset();
+        mCanvasWrapper.reset();
     }
     mSaveStack.reset(nullptr);
     mHighContrastText = false;
@@ -88,13 +96,18 @@
     sk_sp<SkColorSpace> cs = bitmap.refColorSpace();
     std::unique_ptr<SkCanvas> newCanvas =
             std::unique_ptr<SkCanvas>(new SkCanvas(bitmap, SkCanvas::ColorBehavior::kLegacy));
-    std::unique_ptr<SkCanvas> newCanvasWrapper = SkCreateColorSpaceXformCanvas(newCanvas.get(),
-            cs == nullptr ? SkColorSpace::MakeSRGB() : std::move(cs));
+    std::unique_ptr<SkCanvas> newCanvasWrapper;
+    if (cs.get() != nullptr && !cs->isSRGB()) {
+        newCanvasWrapper = SkCreateColorSpaceXformCanvas(newCanvas.get(), std::move(cs));
+    }
+    else if(!uirenderer::Properties::isSkiaEnabled()) {
+        newCanvasWrapper = SkCreateColorSpaceXformCanvas(newCanvas.get(), SkColorSpace::MakeSRGB());
+    }
 
     // deletes the previously owned canvas (if any)
     mCanvasOwned = std::move(newCanvas);
     mCanvasWrapper = std::move(newCanvasWrapper);
-    mCanvas = mCanvasWrapper.get();
+    mCanvas = mCanvasWrapper ? mCanvasWrapper.get() : mCanvasOwned.get();
 
     // clean up the old save stack
     mSaveStack.reset(nullptr);
@@ -531,13 +544,27 @@
 // Canvas draw operations: Bitmaps
 // ----------------------------------------------------------------------------
 
-inline static const SkPaint* addFilter(const SkPaint* origPaint, SkPaint* tmpPaint,
-        sk_sp<SkColorFilter> colorFilter) {
-    if (colorFilter) {
+const SkPaint* SkiaCanvas::addFilter(const SkPaint* origPaint, SkPaint* tmpPaint,
+        sk_sp<SkColorFilter> colorSpaceFilter) {
+    /* We don't apply the colorSpace filter if this canvas is already wrapped with
+     * a SkColorSpaceXformCanvas since it already takes care of converting the
+     * contents of the bitmap into the appropriate colorspace.  The mCanvasWrapper
+     * should only be used if this canvas is backed by a surface/bitmap that is known
+     * to have a non-sRGB colorspace.
+     */
+    if (!mCanvasWrapper && colorSpaceFilter) {
         if (origPaint) {
             *tmpPaint = *origPaint;
         }
-        tmpPaint->setColorFilter(colorFilter);
+
+        if (tmpPaint->getColorFilter()) {
+            tmpPaint->setColorFilter(SkColorFilter::MakeComposeFilter(
+                    tmpPaint->refColorFilter(), colorSpaceFilter));
+            LOG_ALWAYS_FATAL_IF(!tmpPaint->getColorFilter());
+        } else {
+            tmpPaint->setColorFilter(colorSpaceFilter);
+        }
+
         return tmpPaint;
     } else {
         return origPaint;
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index af2c23e..6a01f96 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -37,12 +37,8 @@
      *  @param canvas SkCanvas to handle calls made to this SkiaCanvas. Must
      *      not be NULL. This constructor does not take ownership, so the caller
      *      must guarantee that it remains valid while the SkiaCanvas is valid.
-     *  @param xformToSRGB Indicates if bitmaps should be xformed to the sRGB
-     *      color space before drawing.  This makes sense for software rendering.
-     *      For the picture case, it may make more sense to leave bitmaps as is,
-     *      and handle the xform when replaying the picture.
      */
-    explicit SkiaCanvas(SkCanvas* canvas, XformToSRGB xformToSRGB);
+    explicit SkiaCanvas(SkCanvas* canvas);
 
     virtual ~SkiaCanvas();
 
@@ -182,6 +178,9 @@
     void drawPoints(const float* points, int count, const SkPaint& paint,
             SkCanvas::PointMode mode);
 
+    const SkPaint* addFilter(const SkPaint* origPaint, SkPaint* tmpPaint,
+            sk_sp<SkColorFilter> colorSpaceFilter);
+
     class Clip;
 
     std::unique_ptr<SkCanvas> mCanvasWrapper; // might own a wrapper on the canvas
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index ac8a081..90d98f2 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -98,15 +98,6 @@
     static WARN_UNUSED_RESULT Canvas* create_recording_canvas(int width, int height,
             uirenderer::RenderNode* renderNode = nullptr);
 
-    enum class XformToSRGB {
-        // Transform any Bitmaps to the sRGB color space before drawing.
-        kImmediate,
-
-        // Draw the Bitmap as is.  This likely means that we are recording and that the
-        // transform can be handled at playback time.
-        kDefer,
-    };
-
     /**
      *  Create a new Canvas object which delegates to an SkCanvas.
      *
@@ -114,12 +105,10 @@
      *      delegated to this object. This function will call ref() on the
      *      SkCanvas, and the returned Canvas will unref() it upon
      *      destruction.
-     *  @param xformToSRGB Indicates if bitmaps should be xformed to the sRGB
-     *      color space before drawing.
      *  @return new non-null Canvas Object.  The type of DisplayList produced by this canvas is
      *      determined based on  Properties::getRenderPipelineType().
      */
-    static Canvas* create_canvas(SkCanvas* skiaCanvas, XformToSRGB xformToSRGB);
+    static Canvas* create_canvas(SkCanvas* skiaCanvas);
 
     /**
      *  Provides a Skia SkCanvas interface that acts as a proxy to this Canvas.
diff --git a/libs/hwui/hwui/Typeface.cpp b/libs/hwui/hwui/Typeface.cpp
index d96e376..5dc7c03 100644
--- a/libs/hwui/hwui/Typeface.cpp
+++ b/libs/hwui/hwui/Typeface.cpp
@@ -14,12 +14,6 @@
  * limitations under the License.
  */
 
-/**
- * This is the implementation of the Typeface object. Historically, it has
- * just been SkTypeface, but we are migrating to Minikin. For the time
- * being, that choice is hidden under the USE_MINIKIN compile-time flag.
- */
-
 #include "Typeface.h"
 
 #include <pthread.h>
diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
index ea302a1..fcd72af 100644
--- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp
@@ -17,6 +17,7 @@
 #include "GLFunctorDrawable.h"
 #include "GlFunctorLifecycleListener.h"
 #include "RenderNode.h"
+#include "SkAndroidFrameworkUtils.h"
 #include "SkClipStack.h"
 #include <private/hwui/DrawGlInfo.h>
 #include <GrContext.h>
@@ -49,8 +50,6 @@
         return;
     }
 
-    canvas->flush();
-
     if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) {
         canvas->clear(SK_ColorRED);
         return;
@@ -72,33 +71,33 @@
     info.height = canvasInfo.height();
     mat4.asColMajorf(&info.transform[0]);
 
+    bool clearStencilAfterFunctor = false;
+
     //apply a simple clip with a scissor or a complex clip with a stencil
     SkRegion clipRegion;
     canvas->temporary_internal_getRgnClip(&clipRegion);
     if (CC_UNLIKELY(clipRegion.isComplex())) {
-        //It is only a temporary solution to use a scissor to draw the stencil.
-        //There is a bug 31489986 to implement efficiently non-rectangular clips.
         glDisable(GL_SCISSOR_TEST);
-        glDisable(GL_STENCIL_TEST);
-        glStencilMask(0xff);
+        glStencilMask(0x1);
         glClearStencil(0);
         glClear(GL_STENCIL_BUFFER_BIT);
-        glEnable(GL_SCISSOR_TEST);
-        SkRegion::Cliperator it(clipRegion, ibounds);
-        while (!it.done()) {
-            setScissor(info.height, it.rect());
-            glClearStencil(0x1);
-            glClear(GL_STENCIL_BUFFER_BIT);
-            it.next();
+        bool stencilWritten = SkAndroidFrameworkUtils::clipWithStencil(canvas);
+        canvas->flush();
+        if (stencilWritten) {
+            glStencilMask(0x1);
+            glStencilFunc(GL_EQUAL, 0x1, 0x1);
+            glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+            clearStencilAfterFunctor = true;
+            glEnable(GL_STENCIL_TEST);
+        } else {
+            glDisable(GL_STENCIL_TEST);
         }
-        glDisable(GL_SCISSOR_TEST);
-        glStencilFunc(GL_EQUAL, 0x1, 0xff);
-        glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
-        glEnable(GL_STENCIL_TEST);
     } else if (clipRegion.isEmpty()) {
+        canvas->flush();
         glDisable(GL_STENCIL_TEST);
         glDisable(GL_SCISSOR_TEST);
     } else {
+        canvas->flush();
         glDisable(GL_STENCIL_TEST);
         glEnable(GL_SCISSOR_TEST);
         setScissor(info.height, clipRegion.getBounds());
@@ -106,6 +105,15 @@
 
     (*mFunctor)(DrawGlInfo::kModeDraw, &info);
 
+    if (clearStencilAfterFunctor) {
+        //clear stencil buffer as it may be used by Skia
+        glDisable(GL_SCISSOR_TEST);
+        glDisable(GL_STENCIL_TEST);
+        glStencilMask(0x1);
+        glClearStencil(0);
+        glClear(GL_STENCIL_BUFFER_BIT);
+    }
+
     canvas->getGrContext()->resetContext();
  }
 
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index bb41607..4c1d673 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -150,8 +150,17 @@
         if (origPaint) {
             *tmpPaint = *origPaint;
         }
+
+        sk_sp<SkColorFilter> filter;
+        if (colorFilter && tmpPaint->getColorFilter()) {
+            filter = SkColorFilter::MakeComposeFilter(tmpPaint->refColorFilter(), colorFilter);
+            LOG_ALWAYS_FATAL_IF(!filter);
+        } else {
+            filter = colorFilter;
+        }
+
         tmpPaint->setAntiAlias(false);
-        tmpPaint->setColorFilter(colorFilter);
+        tmpPaint->setColorFilter(filter);
         return tmpPaint;
     } else {
         return origPaint;
diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
index c048dda..d84b83d 100644
--- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp
+++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp
@@ -45,8 +45,7 @@
         // record the same text draw into a SkPicture and replay it into a Recording canvas
         SkPictureRecorder recorder;
         SkCanvas* skCanvas = recorder.beginRecording(200, 200, NULL, 0);
-        std::unique_ptr<Canvas> pictCanvas(Canvas::create_canvas(skCanvas,
-                Canvas::XformToSRGB::kDefer));
+        std::unique_ptr<Canvas> pictCanvas(Canvas::create_canvas(skCanvas));
         TestUtils::drawUtf8ToCanvas(pictCanvas.get(), text, paint, 25, 25);
         sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
 
@@ -65,7 +64,7 @@
 
 TEST(SkiaCanvas, drawShadowLayer) {
     auto surface = SkSurface::MakeRasterN32Premul(10, 10);
-    SkiaCanvas canvas(surface->getCanvas(), Canvas::XformToSRGB::kDefer);
+    SkiaCanvas canvas(surface->getCanvas());
 
     // clear to white
     canvas.drawColor(SK_ColorWHITE, SkBlendMode::kSrc);
@@ -108,27 +107,14 @@
     // The result should be less than fully red, since we convert to Adobe RGB at draw time.
     ASSERT_EQ(0xFF0000DC, *adobeSkBitmap.getAddr32(0, 0));
 
-    // Now try in kDefer mode.  This is a little strange given that, in practice, all software
-    // canvases are kImmediate.
-    SkCanvas skCanvas(skBitmap);
-    SkiaCanvas deferCanvas(&skCanvas, Canvas::XformToSRGB::kDefer);
-    deferCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
-    // The result should be as before, since we deferred the conversion to sRGB.
-    ASSERT_EQ(0xFF0000DC, *skBitmap.getAddr32(0, 0));
-
-    // Test picture recording.  We will kDefer the xform at recording time, but handle it when
-    // we playback to the software canvas.
+    // Test picture recording.
     SkPictureRecorder recorder;
     SkCanvas* skPicCanvas = recorder.beginRecording(1, 1, NULL, 0);
-    SkiaCanvas picCanvas(skPicCanvas, Canvas::XformToSRGB::kDefer);
+    SkiaCanvas picCanvas(skPicCanvas);
     picCanvas.drawBitmap(*adobeBitmap, 0, 0, nullptr);
     sk_sp<SkPicture> picture = recorder.finishRecordingAsPicture();
 
-    // Playback to a deferred canvas.  The result should be as before.
-    deferCanvas.asSkCanvas()->drawPicture(picture);
-    ASSERT_EQ(0xFF0000DC, *skBitmap.getAddr32(0, 0));
-
-    // Playback to an immediate canvas.  The result should be fully red.
+    // Playback to an software canvas.  The result should be fully red.
     canvas.asSkCanvas()->drawPicture(picture);
     ASSERT_EQ(0xFF0000FF, *skBitmap.getAddr32(0, 0));
 }
@@ -155,7 +141,7 @@
     // Create a picture canvas.
     SkPictureRecorder recorder;
     SkCanvas* skPicCanvas = recorder.beginRecording(1, 1, NULL, 0);
-    SkiaCanvas picCanvas(skPicCanvas, Canvas::XformToSRGB::kDefer);
+    SkiaCanvas picCanvas(skPicCanvas);
     state = picCanvas.captureCanvasState();
 
     // Verify that we cannot get the CanvasState.
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 7cdde5f..4c3e261 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -2102,9 +2102,7 @@
     private int getMimeType(BufferedInputStream in) throws IOException {
         in.mark(SIGNATURE_CHECK_SIZE);
         byte[] signatureCheckBytes = new byte[SIGNATURE_CHECK_SIZE];
-        if (in.read(signatureCheckBytes) != SIGNATURE_CHECK_SIZE) {
-            throw new EOFException();
-        }
+        in.read(signatureCheckBytes);
         in.reset();
         if (isJpegFormat(signatureCheckBytes)) {
             return IMAGE_TYPE_JPEG;
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 7dd70d4..4ea4e38 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -16,6 +16,7 @@
 
 package android.media;
 
+import android.annotation.IntDef;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.AssetFileDescriptor;
@@ -27,6 +28,8 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 import java.util.Map;
 
@@ -246,16 +249,67 @@
      * {@link #OPTION_CLOSEST} often has larger performance overhead compared
      * to the other options if there is no sync frame located at timeUs.
      *
-     * @return A Bitmap containing a representative video frame, which 
+     * @return A Bitmap containing a representative video frame, which
      *         can be null, if such a frame cannot be retrieved.
      */
-    public Bitmap getFrameAtTime(long timeUs, int option) {
+    public Bitmap getFrameAtTime(long timeUs, @Option int option) {
         if (option < OPTION_PREVIOUS_SYNC ||
             option > OPTION_CLOSEST) {
             throw new IllegalArgumentException("Unsupported option: " + option);
         }
 
-        return _getFrameAtTime(timeUs, option);
+        return _getFrameAtTime(timeUs, option, -1 /*dst_width*/, -1 /*dst_height*/);
+    }
+
+    /**
+     * Retrieve a video frame near a given timestamp scaled to a desired size.
+     * Call this method after setDataSource(). This method finds a representative
+     * frame close to the given time position by considering the given option
+     * if possible, and returns it as a bitmap with same aspect ratio as the source
+     * while scaling it so that it fits into the desired size of dst_width by dst_height.
+     * This is useful for generating a thumbnail for an input data source or just to
+     * obtain a scaled frame at the given time position.
+     *
+     * @param timeUs The time position in microseconds where the frame will be retrieved.
+     * When retrieving the frame at the given time position, there is no
+     * guarantee that the data source has a frame located at the position.
+     * When this happens, a frame nearby will be returned. If timeUs is
+     * negative, time position and option will ignored, and any frame
+     * that the implementation considers as representative may be returned.
+     *
+     * @param option a hint on how the frame is found. Use
+     * {@link #OPTION_PREVIOUS_SYNC} if one wants to retrieve a sync frame
+     * that has a timestamp earlier than or the same as timeUs. Use
+     * {@link #OPTION_NEXT_SYNC} if one wants to retrieve a sync frame
+     * that has a timestamp later than or the same as timeUs. Use
+     * {@link #OPTION_CLOSEST_SYNC} if one wants to retrieve a sync frame
+     * that has a timestamp closest to or the same as timeUs. Use
+     * {@link #OPTION_CLOSEST} if one wants to retrieve a frame that may
+     * or may not be a sync frame but is closest to or the same as timeUs.
+     * {@link #OPTION_CLOSEST} often has larger performance overhead compared
+     * to the other options if there is no sync frame located at timeUs.
+     *
+     * @param dstWidth expected output bitmap width
+     * @param dstHeight expected output bitmap height
+     * @return A Bitmap of size not larger than dstWidth by dstHeight containing a
+     *         scaled video frame, which can be null, if such a frame cannot be retrieved.
+     * @throws IllegalArgumentException if passed in invalid option or width by height
+     *         is less than or equal to 0.
+     */
+    public Bitmap getScaledFrameAtTime(
+            long timeUs, @Option int option, int dstWidth, int dstHeight) {
+        if (option < OPTION_PREVIOUS_SYNC ||
+            option > OPTION_CLOSEST) {
+            throw new IllegalArgumentException("Unsupported option: " + option);
+        }
+        if (dstWidth <= 0) {
+            throw new IllegalArgumentException("Invalid width: " + dstWidth);
+        }
+        if (dstHeight <= 0) {
+            throw new IllegalArgumentException("Invalid height: " + dstHeight);
+        }
+
+        return _getFrameAtTime(timeUs, option, dstWidth, dstHeight);
     }
 
     /**
@@ -273,8 +327,8 @@
      * negative, time position and option will ignored, and any frame
      * that the implementation considers as representative may be returned.
      *
-     * @return A Bitmap containing a representative video frame, which
-     *         can be null, if such a frame cannot be retrieved.
+     * @return A Bitmap of size dst_widthxdst_height containing a representative
+     *         video frame, which can be null, if such a frame cannot be retrieved.
      *
      * @see #getFrameAtTime(long, int)
      */
@@ -297,17 +351,16 @@
      * @see #getFrameAtTime(long, int)
      */
     public Bitmap getFrameAtTime() {
-        return getFrameAtTime(-1, OPTION_CLOSEST_SYNC);
+        return _getFrameAtTime(-1, OPTION_CLOSEST_SYNC, -1 /*dst_width*/, -1 /*dst_height*/);
     }
 
-    private native Bitmap _getFrameAtTime(long timeUs, int option);
+    private native Bitmap _getFrameAtTime(long timeUs, int option, int width, int height);
 
-    
     /**
      * Call this method after setDataSource(). This method finds the optional
      * graphic or album/cover art associated associated with the data source. If
      * there are more than one pictures, (any) one of them is returned.
-     * 
+     *
      * @return null if no such graphic is found.
      */
     public byte[] getEmbeddedPicture() {
@@ -377,6 +430,16 @@
      */
     public static final int OPTION_CLOSEST          = 0x03;
 
+    /** @hide */
+    @IntDef(flag = true, prefix = { "OPTION_" }, value = {
+            OPTION_PREVIOUS_SYNC,
+            OPTION_NEXT_SYNC,
+            OPTION_CLOSEST_SYNC,
+            OPTION_CLOSEST,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Option {}
+
     /*
      * Do not change these metadata key values without updating their
      * counterparts in include/media/mediametadataretriever.h!
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 849bd01..5a16c36 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -73,6 +73,8 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
+import java.net.CookieHandler;
+import java.net.CookieManager;
 import java.net.HttpCookie;
 import java.net.HttpURLConnection;
 import java.net.InetSocketAddress;
@@ -1004,19 +1006,28 @@
     /**
      * Sets the data source as a content Uri.
      *
-     * @param context the Context to use when resolving the Uri
-     * @param uri the Content URI of the data you want to play
-     * @param headers the headers to be sent together with the request for the data
-     *                The headers must not include cookies. Instead, use the cookies param.
-     * @param cookies the cookies to be sent together with the request
-     * @throws IllegalStateException if it is called in an invalid state
-     * @throws NullPointerException  if context or uri is null
-     * @throws IOException           if uri has a file scheme and an I/O error occurs
+     * To provide cookies for the subsequent HTTP requests, you can install your own default cookie
+     * handler and use other variants of setDataSource APIs instead. Alternatively, you can use
+     * this API to pass the cookies as a list of HttpCookie. If the app has not installed
+     * a CookieHandler already, this API creates a CookieManager and populates its CookieStore with
+     * the provided cookies. If the app has installed its own handler already, this API requires the
+     * handler to be of CookieManager type such that the API can update the manager’s CookieStore.
      *
      * <p><strong>Note</strong> that the cross domain redirection is allowed by default,
      * but that can be changed with key/value pairs through the headers parameter with
      * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to
      * disallow or allow cross domain redirection.
+     *
+     * @param context the Context to use when resolving the Uri
+     * @param uri the Content URI of the data you want to play
+     * @param headers the headers to be sent together with the request for the data
+     *                The headers must not include cookies. Instead, use the cookies param.
+     * @param cookies the cookies to be sent together with the request
+     * @throws IllegalArgumentException if cookies are provided and the installed handler is not
+     *                                  a CookieManager
+     * @throws IllegalStateException    if it is called in an invalid state
+     * @throws NullPointerException     if context or uri is null
+     * @throws IOException              if uri has a file scheme and an I/O error occurs
      */
     public void setDataSource(@NonNull Context context, @NonNull Uri uri,
             @Nullable Map<String, String> headers, @Nullable List<HttpCookie> cookies)
@@ -1029,6 +1040,14 @@
             throw new NullPointerException("uri param can not be null.");
         }
 
+        if (cookies != null) {
+            CookieHandler cookieHandler = CookieHandler.getDefault();
+            if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) {
+                throw new IllegalArgumentException("The cookie handler has to be of CookieManager "
+                        + "type when cookies are provided.");
+            }
+        }
+
         // The context and URI usually belong to the calling user. Get a resolver for that user
         // and strip out the userId from the URI if present.
         final ContentResolver resolver = context.getContentResolver();
@@ -1064,15 +1083,15 @@
     /**
      * Sets the data source as a content Uri.
      *
-     * @param context the Context to use when resolving the Uri
-     * @param uri the Content URI of the data you want to play
-     * @param headers the headers to be sent together with the request for the data
-     * @throws IllegalStateException if it is called in an invalid state
-     *
      * <p><strong>Note</strong> that the cross domain redirection is allowed by default,
      * but that can be changed with key/value pairs through the headers parameter with
      * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to
      * disallow or allow cross domain redirection.
+     *
+     * @param context the Context to use when resolving the Uri
+     * @param uri the Content URI of the data you want to play
+     * @param headers the headers to be sent together with the request for the data
+     * @throws IllegalStateException if it is called in an invalid state
      */
     public void setDataSource(@NonNull Context context, @NonNull Uri uri,
             @Nullable Map<String, String> headers)
@@ -1093,15 +1112,15 @@
     /**
      * Sets the data source (file-path or http/rtsp URL) to use.
      *
-     * @param path the path of the file, or the http/rtsp URL of the stream you want to play
-     * @throws IllegalStateException if it is called in an invalid state
-     *
      * <p>When <code>path</code> refers to a local file, the file may actually be opened by a
      * process other than the calling application.  This implies that the pathname
      * should be an absolute path (as any other process runs with unspecified current working
      * directory), and that the pathname should reference a world-readable file.
      * As an alternative, the application could first open the file for reading,
      * and then use the file descriptor form {@link #setDataSource(FileDescriptor)}.
+     *
+     * @param path the path of the file, or the http/rtsp URL of the stream you want to play
+     * @throws IllegalStateException if it is called in an invalid state
      */
     public void setDataSource(String path)
             throws IOException, IllegalArgumentException, SecurityException, IllegalStateException {
diff --git a/media/java/android/media/browse/MediaBrowser.java b/media/java/android/media/browse/MediaBrowser.java
index 9862564..2bccd88 100644
--- a/media/java/android/media/browse/MediaBrowser.java
+++ b/media/java/android/media/browse/MediaBrowser.java
@@ -256,7 +256,13 @@
      */
     private void forceCloseConnection() {
         if (mServiceConnection != null) {
-            mContext.unbindService(mServiceConnection);
+            try {
+                mContext.unbindService(mServiceConnection);
+            } catch (IllegalArgumentException e) {
+                if (DBG) {
+                    Log.d(TAG, "unbindService failed", e);
+                }
+            }
         }
         mState = CONNECT_STATE_DISCONNECTED;
         mServiceConnection = null;
@@ -445,6 +451,9 @@
         ResultReceiver receiver = new ResultReceiver(mHandler) {
             @Override
             protected void onReceiveResult(int resultCode, Bundle resultData) {
+                if (!isConnected()) {
+                    return;
+                }
                 if (resultCode != 0 || resultData == null
                         || !resultData.containsKey(MediaBrowserService.KEY_MEDIA_ITEM)) {
                     cb.onError(mediaId);
diff --git a/media/java/android/media/tv/ITvInputSessionWrapper.java b/media/java/android/media/tv/ITvInputSessionWrapper.java
index 07cfbda..df87e0f 100644
--- a/media/java/android/media/tv/ITvInputSessionWrapper.java
+++ b/media/java/android/media/tv/ITvInputSessionWrapper.java
@@ -367,7 +367,7 @@
         }
 
         @Override
-        public void onInputEvent(InputEvent event) {
+        public void onInputEvent(InputEvent event, int displayId) {
             if (mTvInputSessionImpl == null) {
                 // The session has been finished.
                 finishInputEvent(event, false);
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index b52906d..4df645d 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -700,6 +700,13 @@
                 new Result<MediaBrowser.MediaItem>(itemId) {
             @Override
             void onResultSent(MediaBrowser.MediaItem item, @ResultFlags int flag) {
+                if (mConnections.get(connection.callbacks.asBinder()) != connection) {
+                    if (DBG) {
+                        Log.d(TAG, "Not sending onLoadItem result for connection that has"
+                                + " been disconnected. pkg=" + connection.pkg + " id=" + itemId);
+                    }
+                    return;
+                }
                 if ((flag & RESULT_FLAG_ON_LOAD_ITEM_NOT_IMPLEMENTED) != 0) {
                     receiver.send(RESULT_ERROR, null);
                     return;
diff --git a/media/jni/android_media_MediaMetadataRetriever.cpp b/media/jni/android_media_MediaMetadataRetriever.cpp
index 71f3856..4659ae1 100644
--- a/media/jni/android_media_MediaMetadataRetriever.cpp
+++ b/media/jni/android_media_MediaMetadataRetriever.cpp
@@ -244,9 +244,11 @@
     }
 }
 
-static jobject android_media_MediaMetadataRetriever_getFrameAtTime(JNIEnv *env, jobject thiz, jlong timeUs, jint option)
+static jobject android_media_MediaMetadataRetriever_getFrameAtTime(
+        JNIEnv *env, jobject thiz, jlong timeUs, jint option, jint dst_width, jint dst_height)
 {
-    ALOGV("getFrameAtTime: %lld us option: %d", (long long)timeUs, option);
+    ALOGV("getFrameAtTime: %lld us option: %d dst width: %d heigh: %d",
+            (long long)timeUs, option, dst_width, dst_height);
     MediaMetadataRetriever* retriever = getRetriever(env, thiz);
     if (retriever == 0) {
         jniThrowException(env, "java/lang/IllegalStateException", "No retriever available");
@@ -274,15 +276,19 @@
                         fields.createConfigMethod,
                         GraphicsJNI::colorTypeToLegacyBitmapConfig(kRGB_565_SkColorType));
 
-    uint32_t width, height;
+    uint32_t width, height, displayWidth, displayHeight;
     bool swapWidthAndHeight = false;
     if (videoFrame->mRotationAngle == 90 || videoFrame->mRotationAngle == 270) {
         width = videoFrame->mHeight;
         height = videoFrame->mWidth;
         swapWidthAndHeight = true;
+        displayWidth = videoFrame->mDisplayHeight;
+        displayHeight = videoFrame->mDisplayWidth;
     } else {
         width = videoFrame->mWidth;
         height = videoFrame->mHeight;
+        displayWidth = videoFrame->mDisplayWidth;
+        displayHeight = videoFrame->mDisplayHeight;
     }
 
     jobject jBitmap = env->CallStaticObjectMethod(
@@ -308,22 +314,26 @@
            videoFrame->mHeight,
            videoFrame->mRotationAngle);
 
-    if (videoFrame->mDisplayWidth  != videoFrame->mWidth ||
-        videoFrame->mDisplayHeight != videoFrame->mHeight) {
-        uint32_t displayWidth = videoFrame->mDisplayWidth;
-        uint32_t displayHeight = videoFrame->mDisplayHeight;
-        if (swapWidthAndHeight) {
-            displayWidth = videoFrame->mDisplayHeight;
-            displayHeight = videoFrame->mDisplayWidth;
-        }
+    if (dst_width <= 0 || dst_height <= 0) {
+        dst_width = displayWidth;
+        dst_height = displayHeight;
+    } else {
+        float factor = std::min((float)dst_width / (float)displayWidth,
+                (float)dst_height / (float)displayHeight);
+        dst_width = std::round(displayWidth * factor);
+        dst_height = std::round(displayHeight * factor);
+    }
+
+    if ((uint32_t)dst_width != videoFrame->mWidth ||
+        (uint32_t)dst_height != videoFrame->mHeight) {
         ALOGV("Bitmap dimension is scaled from %dx%d to %dx%d",
-                width, height, displayWidth, displayHeight);
+                width, height, dst_width, dst_height);
         jobject scaledBitmap = env->CallStaticObjectMethod(fields.bitmapClazz,
-                                    fields.createScaledBitmapMethod,
-                                    jBitmap,
-                                    displayWidth,
-                                    displayHeight,
-                                    true);
+                                fields.createScaledBitmapMethod,
+                                jBitmap,
+                                dst_width,
+                                dst_height,
+                                true);
         return scaledBitmap;
     }
 
@@ -474,7 +484,7 @@
 
         {"setDataSource",   "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaMetadataRetriever_setDataSourceFD},
         {"_setDataSource",   "(Landroid/media/MediaDataSource;)V", (void *)android_media_MediaMetadataRetriever_setDataSourceCallback},
-        {"_getFrameAtTime", "(JI)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
+        {"_getFrameAtTime", "(JIII)Landroid/graphics/Bitmap;", (void *)android_media_MediaMetadataRetriever_getFrameAtTime},
         {"extractMetadata", "(I)Ljava/lang/String;", (void *)android_media_MediaMetadataRetriever_extractMetadata},
         {"getEmbeddedPicture", "(I)[B", (void *)android_media_MediaMetadataRetriever_getEmbeddedPicture},
         {"release",         "()V", (void *)android_media_MediaMetadataRetriever_release},
diff --git a/packages/CaptivePortalLogin/OWNERS b/packages/CaptivePortalLogin/OWNERS
index fa26997..2d71c20 100644
--- a/packages/CaptivePortalLogin/OWNERS
+++ b/packages/CaptivePortalLogin/OWNERS
@@ -1,6 +1,9 @@
 set noparent
 
 per-file Android.mk = build.master@android.com
+per-file Android.mk = ek@google.com
+per-file Android.mk = hugobenichi@google.com
+per-file Android.mk = lorenzo@google.com
 
 ek@google.com
 hugobenichi@google.com
diff --git a/packages/CarrierDefaultApp/AndroidManifest.xml b/packages/CarrierDefaultApp/AndroidManifest.xml
index c309133..1cd7b61 100644
--- a/packages/CarrierDefaultApp/AndroidManifest.xml
+++ b/packages/CarrierDefaultApp/AndroidManifest.xml
@@ -34,6 +34,7 @@
             <intent-filter>
                 <action android:name="com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED" />
                 <action android:name="com.android.internal.telephony.CARRIER_SIGNAL_RESET" />
+                <action android:name="com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE" />
                 <action android:name="android.intent.action.LOCALE_CHANGED" />
             </intent-filter>
         </receiver>
@@ -43,10 +44,24 @@
             android:name="com.android.carrierdefaultapp.CaptivePortalLoginActivity"
             android:label="@string/action_bar_label"
             android:theme="@style/AppTheme"
-            android:configChanges="keyboardHidden|orientation|screenSize" >
+            android:configChanges="keyboardHidden|orientation|screenSize">
             <intent-filter>
                 <category android:name="android.intent.category.DEFAULT"/>
             </intent-filter>
         </activity>
+
+        <activity-alias
+            android:name="com.android.carrierdefaultapp.URLHandlerActivity"
+            android:targetActivity="com.android.carrierdefaultapp.CaptivePortalLoginActivity"
+            android:enabled="false" >
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT"/>
+                <category android:name="android.intent.category.BROWSABLE" />
+                <data android:scheme="http" />
+                <data android:scheme="https" />
+                <data android:host="*" />
+            </intent-filter>
+        </activity-alias>
     </application>
 </manifest>
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 6194b87..b0052cc 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -20,6 +20,9 @@
 import android.app.LoadedApk;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
@@ -34,6 +37,7 @@
 import android.telephony.CarrierConfigManager;
 import android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.TypedValue;
@@ -68,7 +72,7 @@
     private static final boolean DBG = true;
 
     private static final int SOCKET_TIMEOUT_MS = 10 * 1000;
-    public static final int NETWORK_REQUEST_TIMEOUT_MS = 5 * 1000;
+    private static final int NETWORK_REQUEST_TIMEOUT_MS = 5 * 1000;
 
     private URL mUrl;
     private Network mNetwork;
@@ -188,16 +192,19 @@
             CarrierActionUtils.applyCarrierAction(
                     CarrierActionUtils.CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS, getIntent(),
                     getApplicationContext());
-
+            CarrierActionUtils.applyCarrierAction(
+                    CarrierActionUtils.CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER, getIntent(),
+                    getApplicationContext());
+            CarrierActionUtils.applyCarrierAction(
+                    CarrierActionUtils.CARRIER_ACTION_DEREGISTER_DEFAULT_NETWORK_AVAIL, getIntent(),
+                    getApplicationContext());
         }
         finishAndRemoveTask();
     }
 
     private URL getUrlForCaptivePortal() {
         String url = getIntent().getStringExtra(TelephonyIntents.EXTRA_REDIRECTION_URL_KEY);
-        if (url.isEmpty()) {
-            url = mCm.getCaptivePortalServerUrl();
-        }
+        if (TextUtils.isEmpty(url)) url = mCm.getCaptivePortalServerUrl();
         final CarrierConfigManager configManager = getApplicationContext()
                 .getSystemService(CarrierConfigManager.class);
         final int subId = getIntent().getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
@@ -437,6 +444,27 @@
         }
     }
 
+    /**
+     * This alias presents the target activity, CaptivePortalLoginActivity, as a independent
+     * entity with its own intent filter to handle URL links. This alias will be enabled/disabled
+     * dynamically to handle url links based on the network conditions.
+     */
+    public static String getAlias(Context context) {
+        try {
+            PackageInfo p = context.getPackageManager().getPackageInfo(context.getPackageName(),
+                    PackageManager.GET_ACTIVITIES | PackageManager.MATCH_DISABLED_COMPONENTS);
+            for (ActivityInfo activityInfo : p.activities) {
+                String targetActivity = activityInfo.targetActivity;
+                if (CaptivePortalLoginActivity.class.getName().equals(targetActivity)) {
+                    return activityInfo.name;
+                }
+            }
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
     private static void logd(String s) {
         Rlog.d(TAG, s);
     }
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
index 0213306..a2bf964 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
@@ -19,8 +19,10 @@
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.telephony.SubscriptionManager;
@@ -49,6 +51,10 @@
     public static final int CARRIER_ACTION_SHOW_PORTAL_NOTIFICATION          = 4;
     public static final int CARRIER_ACTION_SHOW_NO_DATA_SERVICE_NOTIFICATION = 5;
     public static final int CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS          = 6;
+    public static final int CARRIER_ACTION_ENABLE_DEFAULT_URL_HANDLER        = 7;
+    public static final int CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER       = 8;
+    public static final int CARRIER_ACTION_REGISTER_DEFAULT_NETWORK_AVAIL    = 9;
+    public static final int CARRIER_ACTION_DEREGISTER_DEFAULT_NETWORK_AVAIL  = 10;
 
     public static void applyCarrierAction(int actionIdx, Intent intent, Context context) {
         switch (actionIdx) {
@@ -73,6 +79,18 @@
             case CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS:
                 onCancelAllNotifications(context);
                 break;
+            case CARRIER_ACTION_ENABLE_DEFAULT_URL_HANDLER:
+                onEnableDefaultURLHandler(context);
+                break;
+            case CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER:
+                onDisableDefaultURLHandler(context);
+                break;
+            case CARRIER_ACTION_REGISTER_DEFAULT_NETWORK_AVAIL:
+                onRegisterDefaultNetworkAvail(intent, context);
+                break;
+            case CARRIER_ACTION_DEREGISTER_DEFAULT_NETWORK_AVAIL:
+                onDeregisterDefaultNetworkAvail(intent, context);
+                break;
             default:
                 loge("unsupported carrier action index: " + actionIdx);
         }
@@ -94,6 +112,38 @@
         telephonyMgr.carrierActionSetMeteredApnsEnabled(subId, ENABLE);
     }
 
+    private static void onEnableDefaultURLHandler(Context context) {
+        logd("onEnableDefaultURLHandler");
+        final PackageManager pm = context.getPackageManager();
+        pm.setComponentEnabledSetting(
+                new ComponentName(context, CaptivePortalLoginActivity.getAlias(context)),
+                PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
+    }
+
+    private static void onDisableDefaultURLHandler(Context context) {
+        logd("onDisableDefaultURLHandler");
+        final PackageManager pm = context.getPackageManager();
+        pm.setComponentEnabledSetting(
+                new ComponentName(context, CaptivePortalLoginActivity.getAlias(context)),
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
+    }
+
+    private static void onRegisterDefaultNetworkAvail(Intent intent, Context context) {
+        int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+                SubscriptionManager.getDefaultVoiceSubscriptionId());
+        logd("onRegisterDefaultNetworkAvail subId: " + subId);
+        final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
+        telephonyMgr.carrierActionReportDefaultNetworkStatus(subId, true);
+    }
+
+    private static void onDeregisterDefaultNetworkAvail(Intent intent, Context context) {
+        int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+                SubscriptionManager.getDefaultVoiceSubscriptionId());
+        logd("onDeregisterDefaultNetworkAvail subId: " + subId);
+        final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
+        telephonyMgr.carrierActionReportDefaultNetworkStatus(subId, false);
+    }
+
     private static void onDisableRadio(Intent intent, Context context) {
         int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
                 SubscriptionManager.getDefaultVoiceSubscriptionId());
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
index d5d0b79..02c61d7 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
@@ -22,7 +22,6 @@
 import android.telephony.Rlog;
 import android.text.TextUtils;
 import android.util.Log;
-import android.util.Pair;
 
 import com.android.internal.telephony.TelephonyIntents;
 import com.android.internal.util.ArrayUtils;
@@ -95,6 +94,12 @@
                     configs = b.getStringArray(CarrierConfigManager
                             .KEY_CARRIER_DEFAULT_ACTIONS_ON_RESET);
                     break;
+                case TelephonyIntents.ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE:
+                    configs = b.getStringArray(CarrierConfigManager
+                            .KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE);
+                    arg1 = String.valueOf(intent.getBooleanExtra(TelephonyIntents
+                            .EXTRA_DEFAULT_NETWORK_AVAILABLE_KEY, false));
+                    break;
                 default:
                     Rlog.e(TAG, "load carrier config failure with un-configured key: " +
                             intent.getAction());
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
index 9b5982b9..f243b51 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDatabase.java
@@ -34,6 +34,7 @@
 import android.mtp.MtpObjectInfo;
 import android.net.Uri;
 import android.provider.DocumentsContract;
+import android.provider.MetadataReader;
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsContract.Root;
 
@@ -900,6 +901,9 @@
                 protectionState == MtpConstants.PROTECTION_STATUS_NONE) {
             flag |= Document.FLAG_DIR_SUPPORTS_CREATE;
         }
+        if (MetadataReader.isSupportedMimeType(mimeType)) {
+            flag |= Document.FLAG_SUPPORTS_METADATA;
+        }
         if (thumbnailSize > 0) {
             flag |= Document.FLAG_SUPPORTS_THUMBNAIL;
         }
diff --git a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
index eb2d8aa..8c8116b 100644
--- a/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
+++ b/packages/MtpDocumentsProvider/src/com/android/mtp/MtpDocumentsProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.mtp;
 
+import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -37,11 +38,12 @@
 import android.os.ParcelFileDescriptor;
 import android.os.ProxyFileDescriptorCallback;
 import android.os.storage.StorageManager;
+import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsContract.Path;
 import android.provider.DocumentsContract.Root;
-import android.provider.DocumentsContract;
 import android.provider.DocumentsProvider;
+import android.provider.MetadataReader;
 import android.provider.Settings;
 import android.system.ErrnoException;
 import android.system.OsConstants;
@@ -50,14 +52,16 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
+import libcore.io.IoUtils;
+
 import java.io.FileNotFoundException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.TimeoutException;
-import libcore.io.IoUtils;
 
 /**
  * DocumentsProvider for MTP devices.
@@ -107,7 +111,7 @@
         mResources = getContext().getResources();
         mMtpManager = new MtpManager(getContext());
         mResolver = getContext().getContentResolver();
-        mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
+        mDeviceToolkits = new HashMap<>();
         mDatabase = new MtpDatabase(getContext(), MtpDatabaseConstants.FLAG_DATABASE_IN_FILE);
         mRootScanner = new RootScanner(mResolver, mMtpManager, mDatabase);
         mIntentSender = new ServiceIntentSender(getContext());
@@ -151,7 +155,7 @@
         mResources = resources;
         mMtpManager = mtpManager;
         mResolver = resolver;
-        mDeviceToolkits = new HashMap<Integer, DeviceToolkit>();
+        mDeviceToolkits = new HashMap<>();
         mDatabase = database;
         mRootScanner = new RootScanner(mResolver, mMtpManager, mDatabase);
         mIntentSender = intentSender;
@@ -548,6 +552,29 @@
         }
     }
 
+    @Override
+    public @Nullable Bundle getDocumentMetadata(String docId) throws FileNotFoundException {
+        String mimeType = getDocumentType(docId);
+
+        if (!MetadataReader.isSupportedMimeType(mimeType)) {
+            return null;
+        }
+
+        InputStream stream = null;
+        try {
+            stream = new ParcelFileDescriptor.AutoCloseInputStream(
+                    openDocument(docId, "r", null));
+            Bundle metadata = new Bundle();
+            MetadataReader.getMetadata(metadata, stream, mimeType, null);
+            return metadata;
+        } catch (IOException e) {
+            Log.e(TAG, "An error occurred retrieving the metadata", e);
+            return null;
+        } finally {
+            IoUtils.closeQuietly(stream);
+        }
+    }
+
     void openDevice(int deviceId) throws IOException {
         synchronized (mDeviceListLock) {
             if (mDeviceToolkits.containsKey(deviceId)) {
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
index 7fe6cb9..32b169e 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDatabaseTest.java
@@ -237,7 +237,8 @@
         assertEquals(
                 COLUMN_FLAGS,
                 DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
-                DocumentsContract.Document.FLAG_SUPPORTS_WRITE,
+                DocumentsContract.Document.FLAG_SUPPORTS_WRITE |
+                DocumentsContract.Document.FLAG_SUPPORTS_METADATA,
                 cursor.getInt(9));
         assertEquals(2 * 1024 * 1024, getInt(cursor, COLUMN_SIZE));
         assertEquals(
diff --git a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
index 3fa5eb5..65c86df 100644
--- a/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
+++ b/packages/MtpDocumentsProvider/tests/src/com/android/mtp/MtpDocumentsProviderTest.java
@@ -329,7 +329,8 @@
         assertEquals(
                 DocumentsContract.Document.FLAG_SUPPORTS_DELETE |
                 DocumentsContract.Document.FLAG_SUPPORTS_WRITE |
-                DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL,
+                DocumentsContract.Document.FLAG_SUPPORTS_THUMBNAIL |
+                DocumentsContract.Document.FLAG_SUPPORTS_METADATA,
                 cursor.getInt(4));
         assertEquals(1024 * 1024 * 5, cursor.getInt(5));
     }
@@ -474,7 +475,8 @@
         assertEquals("image/jpeg", cursor.getString(1));
         assertEquals("image.jpg", cursor.getString(2));
         assertEquals(0, cursor.getLong(3));
-        assertEquals(Document.FLAG_SUPPORTS_THUMBNAIL, cursor.getInt(4));
+        assertEquals(Document.FLAG_SUPPORTS_THUMBNAIL
+                | Document.FLAG_SUPPORTS_METADATA, cursor.getInt(4));
         assertEquals(1024 * 1024 * 5, cursor.getInt(5));
 
         cursor.close();
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index c174d5c..bbb437b 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Beskikbaar via %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Gekoppel, geen internet nie"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Toegangspunt is tydelik vol"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Baie stadig"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Stadig"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index e1f6107..61ac02e 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"በ%1$s በኩል የሚገኝ"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"ተገናኝቷል፣ ምንም በይነመረብ የለም"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"የመዳረሻ ነጥብ ለጊዜው ሞልቷል"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"በጣም ቀርፋፋ"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"አዘግይ"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"እሺ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index c7d8250..e341c37 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"‏متوفرة عبر %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"متصلة، ولا يتوفر إنترنت"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"نقطة الدخول ممتلئة مؤقتًا"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"بطيئة جدًا"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"بطيئة"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"موافق"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 34736c6..f63e88f 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s vasitəsilə əlçatandır"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Qoşuludur, internet yoxdur"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Giriş nöqtəsi müvəqqəti olaraq doludur"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Çox Yavaş"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Yavaş"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index bbffe8f..7de4bf7 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Dostupna je preko pristupne tačke %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Veza je uspostavljena, nema interneta"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Pristupna tačka je privremeno zauzeta"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Veoma spora"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Spora"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Potvrdi"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index aaf7e9a..703b066 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Даступна праз %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Падлучана, няма інтэрнэту"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Пункт доступу часова заняты"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Вельмі павольная"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Павольная"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ОК"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 628234c..911823c 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Мрежата е достъпна през „%1$s“"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Установена е връзка – няма достъп до интернет"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Точката за достъп временно е пълна"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Много бавна"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Бавна"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ОK"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 6c8ff8ce..d4629c7 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s এর মাধ্যমে উপলব্ধ"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"সংযুক্ত, ইন্টারনেট নেই"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"এই মুহূর্তে অ্যাক্সেস পয়েন্টের কোনও কানেকশন ফাঁকা নেই"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"খুব ধীরে"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"ধীরে"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ঠিক আছে"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index e6d62d3..b2d56fd 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Dostupan preko %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Povezano. Nema interneta"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Pristupna tačka je privremeno puna"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Veoma sporo"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Sporo"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"UREDU"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 75f464d..c5ece4e 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Disponible mitjançant %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Connectada, sense Internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"El punt d\'accés està temporalment ple"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Molt lenta"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Lenta"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Correcta"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 7af97e1..b9532a3 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Dostupné prostřednictvím %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Připojeno, není k dispozici internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Přístupový bod je dočasně zaplněn"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Velmi pomalá"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Pomalá"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 948096c..61901c9 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Tilgængelig via %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Tilsluttet – intet internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Adgangspunktet er midlertidigt fuldt"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Meget langsom"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Langsom"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 40c5e7a..ece9268 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Verfügbar über %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Verbunden, kein Internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Zugangspunkt vorübergehend voll belegt"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Sehr langsam"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Langsam"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 13072de..1d8bc0f 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Διαθέσιμο μέσω %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Συνδέθηκε, χωρίς διαδίκτυο"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Το σημείο πρόσβασης είναι προσωρινά πλήρες"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Πολύ αργή"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Αργή"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ΟΚ"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index 768dc8d..3811d26 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Available via %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Connected, no Internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Access point temporarily full"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Very slow"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Slow"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index 768dc8d..3811d26 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Available via %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Connected, no Internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Access point temporarily full"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Very slow"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Slow"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index 768dc8d..3811d26 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Available via %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Connected, no Internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Access point temporarily full"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Very slow"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Slow"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 6f990ae..8928600 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Disponible a través de %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Conectado a Wi-Fi, sin conexión a Internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"El punto de acceso está completo temporalmente"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Muy lenta"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Lenta"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Aceptar"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 6d60362..be36b3d 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Disponible a través de %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Conexión sin Internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Punto de acceso temporalmente lleno"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Muy lenta"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Lenta"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Aceptar"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index cee443b..79ff0e2 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Saadaval üksuse %1$s kaudu"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Ühendatud, Interneti-ühendus puudub"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Pääsupunkt on ajutiselt täis"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Väga aeglane"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Aeglane"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Hea"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index d0aa89f..e76f8fd 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s bidez erabilgarri"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Konektatuta, ez dago Interneteko konexiorik"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Sarbide-puntua beteta dago aldi baterako"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Oso motela"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Motela"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Ados"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 266b8ce..da2b8bd 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"‏در دسترس از طریق %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"متصل، بدون اینترنت"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ظرفیت نقطه دسترسی موقتاً تکمیل شده است"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"بسیار آهسته"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"آهسته"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"تأیید"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 185f56a..3285a97 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Käytettävissä seuraavan kautta: %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Yhdistetty, ei internetyhteyttä."</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Yhteyspiste tilapäisesti täynnä"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Hyvin hidas"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Hidas"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 8dc8ead..4be989b 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Accessible par %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Connecté, aucun accès à Internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Le point d\'accès est temporairement plein"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Très lente"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Lente"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 7fdc716..27c3bad 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Disponible via %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Connecté, aucun accès à Internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Point d\'accès temporairement plein"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Très lente"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Lente"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Correct"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 3ae0d6f..0afbf2d 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Dispoñible a través de %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Conectado, pero sen Internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"O punto de acceso está temporalmente cheo"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Moi lenta"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Lenta"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Aceptar"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 9d6c1e8..4c01f7e 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s દ્વારા ઉપલબ્ધ"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"કનેક્ટ કર્યું, કોઈ ઇન્ટરનેટ નથી"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ઍક્સેસ પૉઇન્ટ અસ્થાયીરૂપે ભરાયેલ છે"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"ખૂબ જ ધીમી"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"ધીમી"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ઓકે"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 704a312..93b453d 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s के द्वारा उपलब्ध"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"कनेक्ट किया गया, इंटरनेट नहीं"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"एक्सेस पॉइंट फ़िलहाल भरा हुआ है"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"अत्‍यधिक धीमी"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"धीमी"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ठीक"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 123d996..f3d03c0 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Dostupno putem %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Povezano, bez interneta"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Pristupna je točka privremeno puna"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Vrlo sporo"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Sporo"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"U redu"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index ff700d9..13eae32 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Elérhető a következőn keresztül: %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Csatlakozva, nincs internetelérés"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"A hozzáférési pont átmenetileg megtelt"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Nagyon lassú"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Lassú"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Rendben"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 63fe5ab..dafa130 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Հասանելի է %1$s-ի միջոցով"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Կապակցված է առանց համացանցի"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Հասանելիության կետը ժամանակավորապես լիքն է"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Շատ դանդաղ"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Դանդաղ"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Հաստատել"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 267bbf9..1a4f693 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Tersedia melalui %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Tersambung, tidak ada internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Titik akses penuh untuk sementara"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Sangat Lambat"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Lambat"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Oke"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index ae5f727..7aee8ea 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Í boði í gegnum %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Tengt, enginn internetaðgangur"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Aðgangsstaður tímabundið fullur"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Mjög hægt"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Hægt"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Í lagi"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 4a2d3cb..2c88685 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Disponibile tramite %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Connesso senza Internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Punto di accesso momentaneamente al completo"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Molto lenta"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Lenta"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 7f6f8f5..185f43e 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"‏זמינה דרך %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"מחובר. אין אינטרנט"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"נקודת הגישה מלאה באופן זמני"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"איטית מאוד"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"איטית"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"אישור"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index feb80e9..bb57b9d 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s経由で使用可能"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"接続済み、インターネットは利用できません"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"アクセス ポイントが一時的にいっぱいです"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"とても遅い"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"遅い"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 4d12fa7..e2e6497 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"ხელმისაწვდომია %1$s-ით"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"დაკავშირებულია, ინტერნეტის გარეშე"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"წვდომის წერტილი დროებით გადატვირთულია"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"ძალიან ნელი"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"ნელი"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"კარგი"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 0c7b4ac..424263e 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s арқылы қолжетімді"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Қосылған, интернет жоқ"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Кіру нүктесі уақытша бос емес"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Өте баяу"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Баяу"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Жарайды"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 755859c..f16d381 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"មានតាមរយៈ %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"បានភ្ជាប់ ប៉ុន្តែគ្មានអ៊ីនធឺណិតទេ"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ចំណុចចូលប្រើពេញជាបណ្តោះអាសន្ន"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"យឺតណាស់"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"យឺត"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"យល់ព្រម"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 939e2a9..3b93ac5 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ಮೂಲಕ ಲಭ್ಯವಿದೆ"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ, ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ಪ್ರವೇಶ ಕೇಂದ್ರ ತಾತ್ಕಾಲಿಕವಾಗಿ ಭರ್ತಿಯಾಗಿದೆ"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"ತುಂಬಾ ನಿಧಾನವಾಗಿದೆ"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"ನಿಧಾನ"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ಸರಿ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 972fef1..87b4285 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s을(를) 통해 사용 가능"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"인터넷을 사용하지 않고 연결됨"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"액세스 포인트가 일시적으로 가득 참"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"매우 느림"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"느림"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"확인"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index cff25dd..45f7491 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s аркылуу жеткиликтүү"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Туташып турат, Интернет жок"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Туташуу түйүнү убактылуу толуп калды"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Өтө жай"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Жай"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Жарайт"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 2460154..060947c 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"ມີ​ໃຫ້​ຜ່ານ %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"​ເຊື່ອມ​ຕໍ່​ແລ້ວ,​ ບໍ່​ມີ​ອິນ​ເຕີ​ເນັດ"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ຈຸດການເຂົ້າເຖິງເຕັມຊົ່ວຄາວ"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"ຊ້າຫຼາຍ"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"ຊ້າ"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ຕົກລົງ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index c5a035f..41cc26a 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Pasiekiama naudojant „%1$s“"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Prisijungta, nėra interneto"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Prieigos taškas laikinai visiškai užimtas"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Labai lėtas"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Lėtas"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Gerai"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 89cc958..efce13c 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Pieejams, izmantojot %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Savienots, nav piekļuves internetam"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Piekļuves punkts īslaicīgi ir pilns"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Ļoti lēns"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Lēns"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Labi"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 6bd9c5f..6d0b5be 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Достапно преку %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Поврзана, нема интернет"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Пристапната точка привремено е преоптоварена"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Многу бавна"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Бавна"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Во ред"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 5e0c25c..cf9f94a 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s വഴി ലഭ്യം"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"കണക്റ്റുചെയ്തിരിക്കുന്നു, ഇന്റർനെറ്റില്ല"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ആക്‌സസ് പോയിന്റ് താൽക്കാലികമായി നിറഞ്ഞിരിക്കുന്നു"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"വളരെ കുറഞ്ഞ വേഗത്തിൽ"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"കുറഞ്ഞ വേഗത്തിൽ"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ശരി"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index f0020fe..47c1f5c 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s-р боломжтой"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Холбогдсон, интернэт байхгүй байна"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Хандах цэг түр хугацаанд дүүрсэн байна"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Маш удаан"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Удаан"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ЗА"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index c056d8e..cd4a91f 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s द्वारे उपलब्‍ध"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"कनेक्‍ट केले, इंटरनेट नाही"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"अॅक्सेस पॉइंट तात्पुरते भरलेले"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"खूप हळू"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"हळू"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ठीक आहे"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index a8424ff..e8dcf08 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Tersedia melalui %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Disambungkan, tiada Internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Titik akses penuh buat sementara waktu"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Sangat Perlahan"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Perlahan"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 40cd3e9..5ed00f6 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s မှတစ်ဆင့်ရနိုင်သည်"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"ချိတ်ဆက်ထားသည်၊ အင်တာနက်မရှိ"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ကွန်ရက်ချိတ်ဆက်မှု ယာယီပြည့်နေသည်"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"အလွန်နှေး"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"နှေး"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index b125a9c..0cdffca 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Tilgjengelig via %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Tilkoblet – ingen Internett-forbindelse"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Tilgangspunktet er midlertidig fullt"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Veldig treg"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Treg"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Ok"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 2870d2e..deda8fc 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s मार्फत उपलब्ध"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"जडित, इन्टरनेट चलेको छैन"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"पहुँचसम्बन्धी स्थान अस्थायी रूपमा भरिएको छ"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"धेरै ढिलो"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"बिस्तारै"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ठीक छ"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index b021680..687f717 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Beschikbaar via %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Verbonden, geen internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Toegangspunt tijdelijk vol"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Zeer langzaam"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Langzaam"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Redelijk"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index e1c1a4a..54bb671 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ਰਾਹੀਂ ਉਪਲਬਧ"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"ਕਨੈਕਟ ਕੀਤਾ, ਕੋਈ ਇੰਟਰਨੈਟ ਨਹੀਂ"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ਪਹੁੰਚ ਪੁਆਇੰਟ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਸੰਪੂਰਨ ਰੁਝੇਂਵੇਂ ਵਿੱਚ ਹੈ"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"ਬਹੁਤ ਹੌਲੀ"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"ਹੌਲੀ"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ਠੀਕ ਹੈ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index e4c74503..c95b90a 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Dostępne przez %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Połączono, brak internetu"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Punkt dostępu jest tymczasowo zajęty"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Bardzo wolna"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Wolna"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 7e6e2dc..c534922 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Disponível via %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Conectada, sem Internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Ponto de acesso temporariamente cheio"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Muito lenta"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Lenta"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Ok"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 514dfd1..a173c44 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Disponível através de %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Ligado, sem Internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Ponto de acesso temporariamente cheio"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Muito lenta"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Lenta"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 7e6e2dc..c534922 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Disponível via %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Conectada, sem Internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Ponto de acesso temporariamente cheio"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Muito lenta"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Lenta"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Ok"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index c81673d..c354a564 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Disponibilă prin %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Conectată, fără internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Punctul de acces este temporar plin"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Foarte lentă"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Lentă"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Bine"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 9a28e79..f4448d1 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Доступно через %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Подключено, без Интернета"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"К точке доступа подключено слишком много устройств"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Очень медленная"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Медленная"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ОК"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index afe84f6..9de1c19b 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s හරහා ලබා ගැනීමට හැකිය"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"සම්බන්ධයි, අන්තර්ජාලය නැත"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ප්‍රවේශ ලක්ෂ්‍ය තාවකාලිකව පිරී ඇත"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"ඉතා මන්දගාමී"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"මන්දගාමී"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"හරි"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 1599d80..1a915a5 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"K dispozícii prostredníctvom %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Pripojené, žiadny internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Prístupový bod je dočasne plný"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Veľmi nízka"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Nízka"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index dbeec8a..dcf502a 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Na voljo prek: %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Vzpostavljena povezava, brez interneta"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Dostopna točka je trenutno zasedena"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Zelo počasna"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Počasna"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"V redu"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 85393d7..21306e0 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"E mundshme përmes %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"U lidh, nuk ka internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Pika e qasjes është përkohësisht plot"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Shumë e ulët"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"E ngadaltë"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Në rregull"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 9cbb997..d8ce371 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Доступна је преко приступне тачке %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Веза је успостављена, нема интернета"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Приступна тачка је привремено заузета"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Веома спора"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Спора"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Потврди"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 157e581..13dd146 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Tillgängligt via %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Ansluten, inget internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Åtkomstpunkten har inga platser över för tillfället"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Mycket långsam"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Långsam"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Okej"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index cd6a332..21cc3c8 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Inapatikana kupitia %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Imeunganishwa, hakuna Intaneti"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Lango la mtandao lina shughuli nyingi kwa sasa"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Polepole Sana"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Polepole"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Sawa"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 5014600..7168657 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s வழியாகக் கிடைக்கிறது"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"இணைக்கப்பட்டது, இணையம் இல்லை"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"தற்காலிகமாக அணுகல் புள்ளி நிரம்பியுள்ளது"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"மிகவும் வேகம் குறைவானது"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"வேகம் குறைவு"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"சரி"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index f678a89..cc939dd 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s ద్వారా అందుబాటులో ఉంది"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"కనెక్ట్ చేయబడింది, ఇంటర్నెట్ లేదు"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"ప్రాప్యత పాయింట్ తాత్కాలికంగా పూర్తయింది"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"చాలా నెమ్మది"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"నెమ్మది"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"సరే"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index fa5bdfe..061aa66 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"พร้อมใช้งานผ่านทาง %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"เชื่อมต่อแล้ว ไม่พบอินเทอร์เน็ต"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"จุดเข้าใช้งานเต็มชั่วคราว"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"ช้ามาก"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"ช้า"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ตกลง"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index c0acdc0..03c0b20 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Available sa pamamagitan ng %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Nakakonekta, walang Internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Pansamantalang puno ang access point"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Napakabagal"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Mabagal"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index d288712..06ee507 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s üzerinden kullanılabilir"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Bağlı, İnternet yok"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Erişim noktası geçici olarak dolu"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Çok Yavaş"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Yavaş"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Tamam"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 3be3434..b20de71 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Доступ через %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Під’єднано, але немає доступу до Інтернету"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Точка доступу тимчасово переповнена"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Дуже повільна"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Повільна"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ОК"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 0282d09..446464b 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"‏دستیاب بذریعہ ‎%1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"منسلک، انٹرنیٹ نہیں ہے"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"رسائی پوائنٹ عارضی طور پر فُل ہے"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"بہت سست"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"سست"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"ٹھیک ہے"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 8684b49..c8c2ab4 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"%1$s orqali ishlaydi"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Ulangan, lekin internet aloqasi yo‘q"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Internet kirish nuqtasi vaqtinchalik to‘lgan"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Juda sekin"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Sekin"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"OK"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 836dd3d..e8b608d 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Có sẵn qua %1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Đã kết nối, không có Internet"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Điểm truy cập tạm thời đã đạt đến giới hạn số lượng thiết bị truy cập."</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Rất chậm"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Chậm"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"Khá tốt"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index c7a3d77..8809496 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"可通过%1$s连接"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"已连接,但无法访问互联网"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"接入点暂时满载"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"很慢"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"慢"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"良好"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index bc3348c..1c6d5e5 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"可透過 %1$s 連線"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"已連線,沒有互聯網"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"存取點暫時已滿"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"非常慢"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"慢"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"良好"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 10f2e76..56e1727 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"可透過 %1$s 使用"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"已連線,沒有網際網路"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"存取點暫時滿載"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"非常慢"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"慢"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"確定"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 65b3478..b7fadc2 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -41,6 +41,10 @@
     <string name="available_via_passpoint" msgid="1617440946846329613">"Iyatholakala nge-%1$s"</string>
     <string name="wifi_connected_no_internet" msgid="3149853966840874992">"Kuxhumekile, ayikho i-inthanethi"</string>
     <string name="wifi_ap_unable_to_handle_new_sta" msgid="5348824313514404541">"Iphoyinti lokufinyelela ligcwele okwesikhashana"</string>
+    <!-- no translation found for connected_via_carrier (7583780074526041912) -->
+    <skip />
+    <!-- no translation found for available_via_carrier (1469036129740799053) -->
+    <skip />
     <string name="speed_label_very_slow" msgid="1867055264243608530">"Phansi kakhulu"</string>
     <string name="speed_label_slow" msgid="813109590815810235">"Phansi"</string>
     <string name="speed_label_okay" msgid="2331665440671174858">"KULUNGILE"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index d07da93..7d4bc83 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -97,7 +97,6 @@
 
         // Pairing broadcasts
         addHandler(BluetoothDevice.ACTION_BOND_STATE_CHANGED, new BondStateChangedHandler());
-        addHandler(BluetoothDevice.ACTION_PAIRING_CANCEL, new PairingCancelHandler());
 
         // Fine-grained state broadcasts
         addHandler(BluetoothDevice.ACTION_CLASS_CHANGED, new ClassChangedHandler());
@@ -344,24 +343,6 @@
         }
     }
 
-    private class PairingCancelHandler implements Handler {
-        public void onReceive(Context context, Intent intent, BluetoothDevice device) {
-            if (device == null) {
-                Log.e(TAG, "ACTION_PAIRING_CANCEL with no EXTRA_DEVICE");
-                return;
-            }
-            CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
-            if (cachedDevice == null) {
-                Log.e(TAG, "ACTION_PAIRING_CANCEL with no cached device");
-                return;
-            }
-            int errorMsg = R.string.bluetooth_pairing_error_message;
-            if (context != null && cachedDevice != null) {
-                Utils.showError(context, cachedDevice.getName(), errorMsg);
-            }
-        }
-    }
-
     private class DockEventHandler implements Handler {
         public void onReceive(Context context, Intent intent, BluetoothDevice device) {
             // Remove if unpair device upon undocking
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
new file mode 100644
index 0000000..c167723
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogdSizePreferenceController.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.development;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.SystemProperties;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.content.LocalBroadcastManager;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.R;
+import com.android.settingslib.core.AbstractPreferenceController;
+
+public abstract class AbstractLogdSizePreferenceController extends AbstractPreferenceController
+        implements Preference.OnPreferenceChangeListener {
+    public static final String ACTION_LOGD_SIZE_UPDATED = "com.android.settingslib.development."
+            + "AbstractLogdSizePreferenceController.LOGD_SIZE_UPDATED";
+    public static final String EXTRA_CURRENT_LOGD_VALUE = "CURRENT_LOGD_VALUE";
+
+    private static final String SELECT_LOGD_SIZE_KEY = "select_logd_size";
+    @VisibleForTesting
+    static final String SELECT_LOGD_SIZE_PROPERTY = "persist.logd.size";
+    static final String SELECT_LOGD_TAG_PROPERTY = "persist.log.tag";
+    // Tricky, isLoggable only checks for first character, assumes silence
+    static final String SELECT_LOGD_TAG_SILENCE = "Settings";
+    private static final String SELECT_LOGD_SNET_TAG_PROPERTY = "persist.log.tag.snet_event_log";
+    private static final String SELECT_LOGD_RUNTIME_SNET_TAG_PROPERTY = "log.tag.snet_event_log";
+    private static final String SELECT_LOGD_DEFAULT_SIZE_PROPERTY = "ro.logd.size";
+    @VisibleForTesting
+    static final String SELECT_LOGD_DEFAULT_SIZE_VALUE = "262144";
+    private static final String SELECT_LOGD_SVELTE_DEFAULT_SIZE_VALUE = "65536";
+    // 32768 is merely a menu marker, 64K is our lowest log buffer size we replace it with.
+    private static final String SELECT_LOGD_MINIMUM_SIZE_VALUE = "65536";
+    static final String SELECT_LOGD_OFF_SIZE_MARKER_VALUE = "32768";
+
+    private ListPreference mLogdSize;
+
+    public AbstractLogdSizePreferenceController(Context context) {
+        super(context);
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return true;
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return SELECT_LOGD_SIZE_KEY;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        if (isAvailable()) {
+            mLogdSize = (ListPreference) screen.findPreference(SELECT_LOGD_SIZE_KEY);
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (preference == mLogdSize) {
+            writeLogdSizeOption(newValue);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    public void enablePreference(boolean enabled) {
+        if (isAvailable()) {
+            mLogdSize.setEnabled(enabled);
+        }
+    }
+
+    private String defaultLogdSizeValue() {
+        String defaultValue = SystemProperties.get(SELECT_LOGD_DEFAULT_SIZE_PROPERTY);
+        if ((defaultValue == null) || (defaultValue.length() == 0)) {
+            if (SystemProperties.get("ro.config.low_ram").equals("true")) {
+                defaultValue = SELECT_LOGD_SVELTE_DEFAULT_SIZE_VALUE;
+            } else {
+                defaultValue = SELECT_LOGD_DEFAULT_SIZE_VALUE;
+            }
+        }
+        return defaultValue;
+    }
+
+    public void updateLogdSizeValues() {
+        if (mLogdSize != null) {
+            String currentTag = SystemProperties.get(SELECT_LOGD_TAG_PROPERTY);
+            String currentValue = SystemProperties.get(SELECT_LOGD_SIZE_PROPERTY);
+            if ((currentTag != null) && currentTag.startsWith(SELECT_LOGD_TAG_SILENCE)) {
+                currentValue = SELECT_LOGD_OFF_SIZE_MARKER_VALUE;
+            }
+            LocalBroadcastManager.getInstance(mContext).sendBroadcastSync(
+                    new Intent(ACTION_LOGD_SIZE_UPDATED)
+                            .putExtra(EXTRA_CURRENT_LOGD_VALUE, currentValue));
+            if ((currentValue == null) || (currentValue.length() == 0)) {
+                currentValue = defaultLogdSizeValue();
+            }
+            String[] values = mContext.getResources()
+                    .getStringArray(R.array.select_logd_size_values);
+            String[] titles = mContext.getResources()
+                    .getStringArray(R.array.select_logd_size_titles);
+            int index = 2; // punt to second entry if not found
+            if (SystemProperties.get("ro.config.low_ram").equals("true")) {
+                mLogdSize.setEntries(R.array.select_logd_size_lowram_titles);
+                titles = mContext.getResources()
+                        .getStringArray(R.array.select_logd_size_lowram_titles);
+                index = 1;
+            }
+            String[] summaries = mContext.getResources()
+                    .getStringArray(R.array.select_logd_size_summaries);
+            for (int i = 0; i < titles.length; i++) {
+                if (currentValue.equals(values[i])
+                        || currentValue.equals(titles[i])) {
+                    index = i;
+                    break;
+                }
+            }
+            mLogdSize.setValue(values[index]);
+            mLogdSize.setSummary(summaries[index]);
+        }
+    }
+
+    public void writeLogdSizeOption(Object newValue) {
+        boolean disable = (newValue != null) &&
+                (newValue.toString().equals(SELECT_LOGD_OFF_SIZE_MARKER_VALUE));
+        String currentTag = SystemProperties.get(SELECT_LOGD_TAG_PROPERTY);
+        if (currentTag == null) {
+            currentTag = "";
+        }
+        // filter clean and unstack all references to our setting
+        String newTag = currentTag.replaceAll(
+                ",+" + SELECT_LOGD_TAG_SILENCE, "").replaceFirst(
+                "^" + SELECT_LOGD_TAG_SILENCE + ",*", "").replaceAll(
+                ",+", ",").replaceFirst(
+                ",+$", "");
+        if (disable) {
+            newValue = SELECT_LOGD_MINIMUM_SIZE_VALUE;
+            // Make sure snet_event_log get through first, but do not override
+            String snetValue = SystemProperties.get(SELECT_LOGD_SNET_TAG_PROPERTY);
+            if ((snetValue == null) || (snetValue.length() == 0)) {
+                snetValue = SystemProperties.get(SELECT_LOGD_RUNTIME_SNET_TAG_PROPERTY);
+                if ((snetValue == null) || (snetValue.length() == 0)) {
+                    SystemProperties.set(SELECT_LOGD_SNET_TAG_PROPERTY, "I");
+                }
+            }
+            // Silence all log sources, security logs notwithstanding
+            if (newTag.length() != 0) {
+                newTag = "," + newTag;
+            }
+            // Stack settings, stack to help preserve original value
+            newTag = SELECT_LOGD_TAG_SILENCE + newTag;
+        }
+        if (!newTag.equals(currentTag)) {
+            SystemProperties.set(SELECT_LOGD_TAG_PROPERTY, newTag);
+        }
+        String defaultValue = defaultLogdSizeValue();
+        final String size = ((newValue != null) && (newValue.toString().length() != 0)) ?
+                newValue.toString() : defaultValue;
+        SystemProperties.set(SELECT_LOGD_SIZE_PROPERTY, defaultValue.equals(size) ? "" : size);
+        SystemProperties.set("ctl.start", "logd-reinit");
+        SystemPropPoker.getInstance().poke();
+        updateLogdSizeValues();
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java
new file mode 100644
index 0000000..d813655
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/development/AbstractLogpersistPreferenceController.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.development;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.support.annotation.VisibleForTesting;
+import android.support.v4.content.LocalBroadcastManager;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceScreen;
+import android.text.TextUtils;
+
+import com.android.settingslib.R;
+import com.android.settingslib.core.AbstractPreferenceController;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+import com.android.settingslib.core.lifecycle.LifecycleObserver;
+import com.android.settingslib.core.lifecycle.events.OnCreate;
+import com.android.settingslib.core.lifecycle.events.OnDestroy;
+
+public abstract class AbstractLogpersistPreferenceController extends AbstractPreferenceController
+        implements Preference.OnPreferenceChangeListener, LifecycleObserver, OnCreate, OnDestroy {
+
+    private static final String SELECT_LOGPERSIST_KEY = "select_logpersist";
+    private static final String SELECT_LOGPERSIST_PROPERTY = "persist.logd.logpersistd";
+    @VisibleForTesting
+    static final String ACTUAL_LOGPERSIST_PROPERTY = "logd.logpersistd";
+    @VisibleForTesting
+    static final String SELECT_LOGPERSIST_PROPERTY_SERVICE = "logcatd";
+    private static final String SELECT_LOGPERSIST_PROPERTY_CLEAR = "clear";
+    private static final String SELECT_LOGPERSIST_PROPERTY_STOP = "stop";
+    private static final String SELECT_LOGPERSIST_PROPERTY_BUFFER =
+            "persist.logd.logpersistd.buffer";
+    @VisibleForTesting
+    static final String ACTUAL_LOGPERSIST_PROPERTY_BUFFER = "logd.logpersistd.buffer";
+    private static final String ACTUAL_LOGPERSIST_PROPERTY_ENABLE = "logd.logpersistd.enable";
+
+    private ListPreference mLogpersist;
+    private boolean mLogpersistCleared;
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String currentValue = intent.getStringExtra(
+                    AbstractLogdSizePreferenceController.EXTRA_CURRENT_LOGD_VALUE);
+            onLogdSizeSettingUpdate(currentValue);
+        }
+    };
+
+    public AbstractLogpersistPreferenceController(Context context, Lifecycle lifecycle) {
+        super(context);
+        if (isAvailable()) {
+            lifecycle.addObserver(this);
+        }
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return TextUtils.equals(SystemProperties.get("ro.debuggable", "0"), "1");
+    }
+
+    @Override
+    public String getPreferenceKey() {
+        return SELECT_LOGPERSIST_KEY;
+    }
+
+    @Override
+    public void displayPreference(PreferenceScreen screen) {
+        super.displayPreference(screen);
+        if (isAvailable()) {
+            mLogpersist = (ListPreference) screen.findPreference(SELECT_LOGPERSIST_KEY);
+        }
+    }
+
+    @Override
+    public boolean onPreferenceChange(Preference preference, Object newValue) {
+        if (preference == mLogpersist) {
+            writeLogpersistOption(newValue, false);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        LocalBroadcastManager.getInstance(mContext).registerReceiver(mReceiver,
+                new IntentFilter(AbstractLogdSizePreferenceController.ACTION_LOGD_SIZE_UPDATED));
+    }
+
+    @Override
+    public void onDestroy() {
+        LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mReceiver);
+    }
+
+    public void enablePreference(boolean enabled) {
+        if (isAvailable()) {
+            mLogpersist.setEnabled(enabled);
+        }
+    }
+
+    private void onLogdSizeSettingUpdate(String currentValue) {
+        if (mLogpersist != null) {
+            String currentLogpersistEnable
+                    = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY_ENABLE);
+            if ((currentLogpersistEnable == null)
+                    || !currentLogpersistEnable.equals("true")
+                    || currentValue.equals(
+                    AbstractLogdSizePreferenceController.SELECT_LOGD_OFF_SIZE_MARKER_VALUE)) {
+                writeLogpersistOption(null, true);
+                mLogpersist.setEnabled(false);
+            } else if (DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(mContext)) {
+                mLogpersist.setEnabled(true);
+            }
+        }
+    }
+
+    public void updateLogpersistValues() {
+        if (mLogpersist == null) {
+            return;
+        }
+        String currentValue = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY);
+        if (currentValue == null) {
+            currentValue = "";
+        }
+        String currentBuffers = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY_BUFFER);
+        if ((currentBuffers == null) || (currentBuffers.length() == 0)) {
+            currentBuffers = "all";
+        }
+        int index = 0;
+        if (currentValue.equals(SELECT_LOGPERSIST_PROPERTY_SERVICE)) {
+            index = 1;
+            if (currentBuffers.equals("kernel")) {
+                index = 3;
+            } else if (!currentBuffers.equals("all") &&
+                    !currentBuffers.contains("radio") &&
+                    currentBuffers.contains("security") &&
+                    currentBuffers.contains("kernel")) {
+                index = 2;
+                if (!currentBuffers.contains("default")) {
+                    String[] contains = {"main", "events", "system", "crash"};
+                    for (String type : contains) {
+                        if (!currentBuffers.contains(type)) {
+                            index = 1;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+        mLogpersist.setValue(
+                mContext.getResources().getStringArray(R.array.select_logpersist_values)[index]);
+        mLogpersist.setSummary(
+                mContext.getResources().getStringArray(R.array.select_logpersist_summaries)[index]);
+        if (index != 0) {
+            mLogpersistCleared = false;
+        } else if (!mLogpersistCleared) {
+            // would File.delete() directly but need to switch uid/gid to access
+            SystemProperties.set(ACTUAL_LOGPERSIST_PROPERTY, SELECT_LOGPERSIST_PROPERTY_CLEAR);
+            SystemPropPoker.getInstance().poke();
+            mLogpersistCleared = true;
+        }
+    }
+
+    protected void setLogpersistOff(boolean update) {
+        SystemProperties.set(SELECT_LOGPERSIST_PROPERTY_BUFFER, "");
+        // deal with trampoline of empty properties
+        SystemProperties.set(ACTUAL_LOGPERSIST_PROPERTY_BUFFER, "");
+        SystemProperties.set(SELECT_LOGPERSIST_PROPERTY, "");
+        SystemProperties.set(ACTUAL_LOGPERSIST_PROPERTY,
+                update ? "" : SELECT_LOGPERSIST_PROPERTY_STOP);
+        SystemPropPoker.getInstance().poke();
+        if (update) {
+            updateLogpersistValues();
+        } else {
+            for (int i = 0; i < 3; i++) {
+                String currentValue = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY);
+                if ((currentValue == null) || currentValue.equals("")) {
+                    break;
+                }
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                    // Ignore
+                }
+            }
+        }
+    }
+
+    public void writeLogpersistOption(Object newValue, boolean skipWarning) {
+        if (mLogpersist == null) {
+            return;
+        }
+        String currentTag = SystemProperties.get(
+                AbstractLogdSizePreferenceController.SELECT_LOGD_TAG_PROPERTY);
+        if ((currentTag != null) && currentTag.startsWith(
+                AbstractLogdSizePreferenceController.SELECT_LOGD_TAG_SILENCE)) {
+            newValue = null;
+            skipWarning = true;
+        }
+
+        if ((newValue == null) || newValue.toString().equals("")) {
+            if (skipWarning) {
+                mLogpersistCleared = false;
+            } else if (!mLogpersistCleared) {
+                // if transitioning from on to off, pop up an are you sure?
+                String currentValue = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY);
+                if ((currentValue != null) &&
+                        currentValue.equals(SELECT_LOGPERSIST_PROPERTY_SERVICE)) {
+                    showConfirmationDialog(mLogpersist);
+                    return;
+                }
+            }
+            setLogpersistOff(true);
+            return;
+        }
+
+        String currentBuffer = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY_BUFFER);
+        if ((currentBuffer != null) && !currentBuffer.equals(newValue.toString())) {
+            setLogpersistOff(false);
+        }
+        SystemProperties.set(SELECT_LOGPERSIST_PROPERTY_BUFFER, newValue.toString());
+        SystemProperties.set(SELECT_LOGPERSIST_PROPERTY, SELECT_LOGPERSIST_PROPERTY_SERVICE);
+        SystemPropPoker.getInstance().poke();
+        for (int i = 0; i < 3; i++) {
+            String currentValue = SystemProperties.get(ACTUAL_LOGPERSIST_PROPERTY);
+            if ((currentValue != null)
+                    && currentValue.equals(SELECT_LOGPERSIST_PROPERTY_SERVICE)) {
+                break;
+            }
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                // Ignore
+            }
+        }
+        updateLogpersistValues();
+    }
+
+    public abstract void showConfirmationDialog(ListPreference preference);
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
index 9620a91..fc0a444 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/TileUtils.java
@@ -149,13 +149,6 @@
     public static final String META_DATA_PREFERENCE_TITLE = "com.android.settings.title";
 
     /**
-     * @deprecated Use {@link #META_DATA_PREFERENCE_TITLE} with {@code android:resource}
-     */
-    @Deprecated
-    public static final String META_DATA_PREFERENCE_TITLE_RES_ID =
-            "com.android.settings.title.resid";
-
-    /**
      * Name of the meta-data item that should be set in the AndroidManifest.xml
      * to specify the summary text that should be displayed for the preference.
      */
@@ -176,7 +169,7 @@
      * custom view which should be displayed for the preference. The custom view will be inflated
      * as a remote view.
      *
-     * This also can be used with {@link META_DATA_PREFERENCE_SUMMARY_URI} above, by setting the id
+     * This also can be used with {@link #META_DATA_PREFERENCE_SUMMARY_URI}, by setting the id
      * of the summary TextView to '@android:id/summary'.
      */
     public static final String META_DATA_PREFERENCE_CUSTOM_VIEW =
@@ -411,14 +404,7 @@
                                     metaData.getBoolean(META_DATA_PREFERENCE_ICON_TINTABLE);
                         }
                     }
-                    int resId = 0;
-                    if (metaData.containsKey(META_DATA_PREFERENCE_TITLE_RES_ID)) {
-                        resId = metaData.getInt(META_DATA_PREFERENCE_TITLE_RES_ID);
-                        if (resId != 0) {
-                            title = res.getString(resId);
-                        }
-                    }
-                    if ((resId == 0) && metaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
+                    if (metaData.containsKey(META_DATA_PREFERENCE_TITLE)) {
                         if (metaData.get(META_DATA_PREFERENCE_TITLE) instanceof Integer) {
                             title = res.getString(metaData.getInt(META_DATA_PREFERENCE_TITLE));
                         } else {
@@ -464,12 +450,14 @@
             }
 
             // Set the icon
-            if (iconFromUri != null) {
-                tile.icon = Icon.createWithResource(iconFromUri.first, iconFromUri.second);
-            } else {
-                if (icon == 0) {
+            if (icon == 0) {
+                // Only fallback to activityinfo.icon if metadata does not contain ICON_URI.
+                // ICON_URI should be loaded in app UI when need the icon object.
+                if (!tile.metaData.containsKey(META_DATA_PREFERENCE_ICON_URI)) {
                     icon = activityInfo.icon;
                 }
+            }
+            if (icon != 0) {
                 tile.icon = Icon.createWithResource(activityInfo.packageName, icon);
             }
 
@@ -492,7 +480,7 @@
 
     /**
      * Gets the icon package name and resource id from content provider.
-     * @param Context context
+     * @param context context
      * @param packageName package name of the target activity
      * @param uriString URI for the content provider
      * @param providerMap Maps URI authorities to providers
@@ -522,7 +510,7 @@
 
     /**
      * Gets text associated with the input key from the content provider.
-     * @param Context context
+     * @param context context
      * @param uriString URI for the content provider
      * @param providerMap Maps URI authorities to providers
      * @param key Key mapping to the text in bundle returned by the content provider
diff --git a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSubtypePreference.java b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSubtypePreference.java
index 5fdab29..f824ec7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSubtypePreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/inputmethod/InputMethodSubtypePreference.java
@@ -17,12 +17,12 @@
 package com.android.settingslib.inputmethod;
 
 import android.content.Context;
-import android.support.v14.preference.SwitchPreference;
 import android.support.v7.preference.Preference;
 import android.text.TextUtils;
 import android.view.inputmethod.InputMethodInfo;
 import android.view.inputmethod.InputMethodSubtype;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.inputmethod.InputMethodUtils;
 
 import java.text.Collator;
@@ -39,18 +39,28 @@
 
     public InputMethodSubtypePreference(final Context context, final InputMethodSubtype subtype,
             final InputMethodInfo imi) {
+        this(context,
+                imi.getId() + subtype.hashCode(),
+                InputMethodAndSubtypeUtil.getSubtypeLocaleNameAsSentence(subtype, context, imi),
+                subtype.getLocale(),
+                context.getResources().getConfiguration().locale);
+    }
+
+    @VisibleForTesting
+    InputMethodSubtypePreference(
+            final Context context,
+            final String prefKey,
+            final CharSequence title,
+            final String subtypeLocaleString,
+            final Locale systemLocale) {
         super(context);
         setPersistent(false);
-        setKey(imi.getId() + subtype.hashCode());
-        final CharSequence subtypeLabel =
-                InputMethodAndSubtypeUtil.getSubtypeLocaleNameAsSentence(subtype, context, imi);
-        setTitle(subtypeLabel);
-        final String subtypeLocaleString = subtype.getLocale();
+        setKey(prefKey);
+        setTitle(title);
         if (TextUtils.isEmpty(subtypeLocaleString)) {
             mIsSystemLocale = false;
             mIsSystemLanguage = false;
         } else {
-            final Locale systemLocale = context.getResources().getConfiguration().locale;
             mIsSystemLocale = subtypeLocaleString.equals(systemLocale.toString());
             mIsSystemLanguage = mIsSystemLocale
                     || InputMethodUtils.getLanguageFromLocaleString(subtypeLocaleString)
@@ -76,15 +86,15 @@
             if (!mIsSystemLanguage && rhsPref.mIsSystemLanguage) {
                 return 1;
             }
-            final CharSequence t0 = getTitle();
-            final CharSequence t1 = rhs.getTitle();
-            if (t0 == null && t1 == null) {
-                return Integer.compare(hashCode(), rhs.hashCode());
+            final CharSequence title = getTitle();
+            final CharSequence rhsTitle = rhs.getTitle();
+            final boolean emptyTitle = TextUtils.isEmpty(title);
+            final boolean rhsEmptyTitle = TextUtils.isEmpty(rhsTitle);
+            if (!emptyTitle && !rhsEmptyTitle) {
+                return collator.compare(title.toString(), rhsTitle.toString());
             }
-            if (t0 != null && t1 != null) {
-                return collator.compare(t0.toString(), t1.toString());
-            }
-            return t0 == null ? -1 : 1;
+            // For historical reasons, an empty text needs to be put at the first.
+            return (emptyTitle ? -1 : 0) - (rhsEmptyTitle ? -1 : 0);
         }
         return super.compareTo(rhs);
     }
diff --git a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
index 6eeb593..88adcdb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/utils/ThreadUtils.java
@@ -18,10 +18,14 @@
 import android.os.Handler;
 import android.os.Looper;
 
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
 public class ThreadUtils {
 
     private static volatile Thread sMainThread;
     private static volatile Handler sMainThreadHandler;
+    private static volatile ExecutorService sSingleThreadExecutor;
 
     /**
      * Returns true if the current thread is the UI thread.
@@ -54,6 +58,16 @@
     }
 
     /**
+     * Posts runnable in background using shared background thread pool.
+     */
+    public static void postOnBackgroundThread(Runnable runnable) {
+        if (sSingleThreadExecutor == null) {
+            sSingleThreadExecutor = Executors.newSingleThreadExecutor();
+        }
+        sSingleThreadExecutor.execute(runnable);
+    }
+
+    /**
      * Posts the runnable on the main thread.
      */
     public static void postOnMainThread(Runnable runnable) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index d04ccf2..33af4039 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -37,6 +37,7 @@
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiEnterpriseConfig;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiNetworkScoreCache;
@@ -140,6 +141,9 @@
     static final String KEY_CONFIG = "key_config";
     static final String KEY_FQDN = "key_fqdn";
     static final String KEY_PROVIDER_FRIENDLY_NAME = "key_provider_friendly_name";
+    static final String KEY_IS_CARRIER_AP = "key_is_carrier_ap";
+    static final String KEY_CARRIER_AP_EAP_TYPE = "key_carrier_ap_eap_type";
+    static final String KEY_CARRIER_NAME = "key_carrier_name";
     static final AtomicInteger sLastId = new AtomicInteger(0);
 
     /**
@@ -197,6 +201,13 @@
     private String mFqdn;
     private String mProviderFriendlyName;
 
+    private boolean mIsCarrierAp = false;
+    /**
+     * The EAP type {@link WifiEnterpriseConfig.Eap} associated with this AP if it is a carrier AP.
+     */
+    private int mCarrierApEapType = WifiEnterpriseConfig.Eap.NONE;
+    private String mCarrierName = null;
+
     public AccessPoint(Context context, Bundle savedState) {
         mContext = context;
         mConfig = savedState.getParcelable(KEY_CONFIG);
@@ -233,6 +244,15 @@
         if (savedState.containsKey(KEY_PROVIDER_FRIENDLY_NAME)) {
             mProviderFriendlyName = savedState.getString(KEY_PROVIDER_FRIENDLY_NAME);
         }
+        if (savedState.containsKey(KEY_IS_CARRIER_AP)) {
+            mIsCarrierAp = savedState.getBoolean(KEY_IS_CARRIER_AP);
+        }
+        if (savedState.containsKey(KEY_CARRIER_AP_EAP_TYPE)) {
+            mCarrierApEapType = savedState.getInt(KEY_CARRIER_AP_EAP_TYPE);
+        }
+        if (savedState.containsKey(KEY_CARRIER_NAME)) {
+            mCarrierName = savedState.getString(KEY_CARRIER_NAME);
+        }
         update(mConfig, mInfo, mNetworkInfo);
         updateRssi();
         updateSeen();
@@ -291,6 +311,9 @@
         this.mId = that.mId;
         this.mSpeed = that.mSpeed;
         this.mIsScoredNetworkMetered = that.mIsScoredNetworkMetered;
+        this.mIsCarrierAp = that.mIsCarrierAp;
+        this.mCarrierApEapType = that.mCarrierApEapType;
+        this.mCarrierName = that.mCarrierName;
     }
 
     /**
@@ -604,7 +627,7 @@
 
     public String getSecurityString(boolean concise) {
         Context context = mContext;
-        if (mConfig != null && mConfig.isPasspoint()) {
+        if (isPasspoint() || isPasspointConfig()) {
             return concise ? context.getString(R.string.wifi_security_short_eap) :
                 context.getString(R.string.wifi_security_eap);
         }
@@ -670,6 +693,18 @@
         return null;
     }
 
+    public boolean isCarrierAp() {
+        return mIsCarrierAp;
+    }
+
+    public int getCarrierApEapType() {
+        return mCarrierApEapType;
+    }
+
+    public String getCarrierName() {
+        return mCarrierName;
+    }
+
     public String getSavedNetworkSummary() {
         WifiConfiguration config = mConfig;
         if (config != null) {
@@ -712,6 +747,9 @@
             // This is the active connection on passpoint
             summary.append(getSummary(mContext, getDetailedState(),
                     false, config.providerFriendlyName));
+        } else if (isActive() && config != null && getDetailedState() == DetailedState.CONNECTED
+                && mIsCarrierAp) {
+            summary.append(String.format(mContext.getString(R.string.connected_via_carrier), mCarrierName));
         } else if (isActive()) {
             // This is the active connection on non-passpoint network
             summary.append(getSummary(mContext, getDetailedState(),
@@ -745,6 +783,8 @@
             }
         } else if (config != null && config.getNetworkSelectionStatus().isNotRecommended()) {
             summary.append(mContext.getString(R.string.wifi_disabled_by_recommendation_provider));
+        } else if (mIsCarrierAp) {
+            summary.append(String.format(mContext.getString(R.string.available_via_carrier), mCarrierName));
         } else if (!isReachable()) { // Wifi out of range
             summary.append(mContext.getString(R.string.wifi_not_in_range));
         } else { // In range, not disabled.
@@ -1024,6 +1064,9 @@
         mScanResultCache.put(result.BSSID, result);
         updateRssi();
         mSeen = result.timestamp; // even if the timestamp is old it is still valid
+        mIsCarrierAp = result.isCarrierAp;
+        mCarrierApEapType = result.carrierApEapType;
+        mCarrierName = result.carrierName;
     }
 
     public void saveWifiState(Bundle savedState) {
@@ -1045,6 +1088,9 @@
         if (mProviderFriendlyName != null) {
             savedState.putString(KEY_PROVIDER_FRIENDLY_NAME, mProviderFriendlyName);
         }
+        savedState.putBoolean(KEY_IS_CARRIER_AP, mIsCarrierAp);
+        savedState.putInt(KEY_CARRIER_AP_EAP_TYPE, mCarrierApEapType);
+        savedState.putString(KEY_CARRIER_NAME, mCarrierName);
     }
 
     public void setListener(AccessPointListener listener) {
@@ -1073,6 +1119,12 @@
                 mAccessPointListener.onAccessPointChanged(this);
             }
 
+            // The carrier info in the ScanResult is set by the platform based on the SSID and will
+            // always be the same for all matching scan results.
+            mIsCarrierAp = result.isCarrierAp;
+            mCarrierApEapType = result.carrierApEapType;
+            mCarrierName = result.carrierName;
+
             return true;
         }
         return false;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java b/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
index 731a607..93bf3c7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/TestAccessPointBuilder.java
@@ -53,6 +53,8 @@
     private int mSecurity = AccessPoint.SECURITY_NONE;
     private WifiConfiguration mWifiConfig;
     private WifiInfo mWifiInfo;
+    private boolean mIsCarrierAp = false;
+    private String mCarrierName = null;
 
     Context mContext;
     private ArrayList<ScanResult> mScanResultCache;
@@ -85,6 +87,10 @@
         }
         bundle.putInt(AccessPoint.KEY_SECURITY, mSecurity);
         bundle.putInt(AccessPoint.KEY_SPEED, mSpeed);
+        bundle.putBoolean(AccessPoint.KEY_IS_CARRIER_AP, mIsCarrierAp);
+        if (mCarrierName != null) {
+            bundle.putString(AccessPoint.KEY_CARRIER_NAME, mCarrierName);
+        }
 
         AccessPoint ap = new AccessPoint(mContext, bundle);
         ap.setRssi(mRssi);
@@ -222,4 +228,14 @@
         mScanResultCache = scanResultCache;
         return this;
     }
+
+    public TestAccessPointBuilder setIsCarrierAp(boolean isCarrierAp) {
+        mIsCarrierAp = isCarrierAp;
+        return this;
+    }
+
+    public TestAccessPointBuilder setCarrierName(String carrierName) {
+        mCarrierName = carrierName;
+        return this;
+    }
 }
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodSubtypePreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodSubtypePreferenceTest.java
new file mode 100644
index 0000000..8af027c
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/inputmethod/InputMethodSubtypePreferenceTest.java
@@ -0,0 +1,139 @@
+package com.android.settingslib.inputmethod;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.function.BiConsumer;
+import java.util.function.Function;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InputMethodSubtypePreferenceTest {
+
+    private static final List<InputMethodSubtypePreference> ITEMS_IN_ASCENDING = Arrays.asList(
+            // Subtypes that has the same locale of the system's.
+            createPreference("", "en_US", Locale.US),
+            createPreference("E", "en_US", Locale.US),
+            createPreference("Z", "en_US", Locale.US),
+            // Subtypes that has the same language of the system's.
+            createPreference("", "en", Locale.US),
+            createPreference("E", "en", Locale.US),
+            createPreference("Z", "en", Locale.US),
+            // Subtypes that has different language than the system's.
+            createPreference("", "ja", Locale.US),
+            createPreference("A", "hi_IN", Locale.US),
+            createPreference("B", "", Locale.US),
+            createPreference("E", "ja", Locale.US),
+            createPreference("Z", "ja", Locale.US)
+    );
+    private static final List<InputMethodSubtypePreference> SAME_ITEMS = Arrays.asList(
+            // Subtypes that has different language than the system's.
+            createPreference("A", "ja_JP", Locale.US),
+            createPreference("A", "hi_IN", Locale.US),
+            // Subtypes that has an empty subtype locale string.
+            createPreference("A", "", Locale.US)
+    );
+    private static final Collator COLLATOR = Collator.getInstance(Locale.US);
+
+    @Test
+    public void testComparableOrdering() throws Exception {
+        onAllAdjacentItems(ITEMS_IN_ASCENDING,
+                (x, y) -> assertTrue(
+                        x.getKey() + " is less than " + y.getKey(),
+                        x.compareTo(y, COLLATOR) < 0)
+        );
+    }
+
+    @Test
+    public void testComparableEquality() {
+        onAllAdjacentItems(SAME_ITEMS,
+                (x, y) -> assertTrue(
+                        x.getKey() + " is equal to " + y.getKey(),
+                        x.compareTo(y, COLLATOR) == 0)
+        );
+    }
+
+    @Test
+    public void testComparableContracts() {
+        final Collection<InputMethodSubtypePreference> items = new ArrayList<>();
+        items.addAll(ITEMS_IN_ASCENDING);
+        items.addAll(SAME_ITEMS);
+        items.add(createPreference("", "", Locale.US));
+        items.add(createPreference("A", "en", Locale.US));
+        items.add(createPreference("A", "en_US", Locale.US));
+        items.add(createPreference("E", "hi_IN", Locale.US));
+        items.add(createPreference("E", "en", Locale.US));
+        items.add(createPreference("Z", "en_US", Locale.US));
+
+        assertComparableContracts(
+                items,
+                (x, y) -> x.compareTo(y, COLLATOR),
+                InputMethodSubtypePreference::getKey);
+    }
+
+    private static InputMethodSubtypePreference createPreference(
+            final String subtypeName,
+            final String subtypeLocaleString,
+            final Locale systemLocale) {
+        return new InputMethodSubtypePreference(
+                InstrumentationRegistry.getTargetContext(),
+                subtypeName + "-" + subtypeLocaleString + "-" + systemLocale,
+                subtypeName,
+                subtypeLocaleString,
+                systemLocale);
+    }
+
+    private static <T> void onAllAdjacentItems(final List<T> items, final BiConsumer<T, T> check) {
+        for (int i = 0; i < items.size() - 1; i++) {
+            check.accept(items.get(i), items.get(i + 1));
+        }
+    }
+
+    @FunctionalInterface
+    interface CompareTo<T> {
+        int apply(T t, T u);
+    }
+
+    private static <T> void assertComparableContracts(final Collection<T> items,
+            final CompareTo<T> compareTo, final Function<T, String> name) {
+        for (final T x : items) {
+            final String nameX = name.apply(x);
+            assertTrue("Reflective: " + nameX + " is equal to itself",
+                    compareTo.apply(x, x) == 0);
+            for (final T y : items) {
+                final String nameY = name.apply(y);
+                assertEquals("Asymmetric: " + nameX + " and " + nameY,
+                        Integer.signum(compareTo.apply(x, y)),
+                        -Integer.signum(compareTo.apply(y, x)));
+                for (final T z : items) {
+                    final String nameZ = name.apply(z);
+                    if (compareTo.apply(x, y) > 0 && compareTo.apply(y, z) > 0) {
+                        assertTrue("Transitive: " + nameX + " is greater than " + nameY
+                                        + " and " + nameY + " is greater than " + nameZ
+                                        + " then " + nameX + " is greater than " + nameZ,
+                                compareTo.apply(x, z) > 0);
+                    }
+                    if (compareTo.apply(x, y) == 0) {
+                        assertEquals("Transitive: " + nameX + " and " + nameY + " is same "
+                                        + " then both return the same result for " + nameZ,
+                                Integer.signum(compareTo.apply(x, z)),
+                                Integer.signum(compareTo.apply(y, z)));
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 9645c94..24f0c7a 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -35,6 +35,7 @@
 import android.net.WifiKey;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiEnterpriseConfig;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiNetworkScoreCache;
 import android.net.wifi.WifiSsid;
@@ -491,6 +492,75 @@
                 R.string.wifi_check_password_try_again));
     }
 
+    @Test
+    public void testSummaryString_showsAvaiableViaCarrier() {
+        String carrierName = "Test Carrier";
+        ScanResult result = new ScanResult();
+        result.BSSID = "00:11:22:33:44:55";
+        result.capabilities = "EAP";
+        result.isCarrierAp = true;
+        result.carrierApEapType = WifiEnterpriseConfig.Eap.SIM;
+        result.carrierName = carrierName;
+
+        AccessPoint ap = new AccessPoint(mContext, result);
+        assertThat(ap.getSummary()).isEqualTo(String.format(mContext.getString(
+                R.string.available_via_carrier), carrierName));
+        assertThat(ap.isCarrierAp()).isEqualTo(true);
+        assertThat(ap.getCarrierApEapType()).isEqualTo(WifiEnterpriseConfig.Eap.SIM);
+        assertThat(ap.getCarrierName()).isEqualTo(carrierName);
+    }
+
+    @Test
+    public void testSummaryString_showsConnectedViaCarrier() {
+        int networkId = 123;
+        int rssi = -55;
+        String carrierName = "Test Carrier";
+        WifiConfiguration config = new WifiConfiguration();
+        config.networkId = networkId;
+        WifiInfo wifiInfo = new WifiInfo();
+        wifiInfo.setNetworkId(networkId);
+        wifiInfo.setRssi(rssi);
+
+        NetworkInfo networkInfo =
+                new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
+        networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", "");
+
+        AccessPoint ap = new TestAccessPointBuilder(mContext)
+                .setNetworkInfo(networkInfo)
+                .setNetworkId(networkId)
+                .setRssi(rssi)
+                .setWifiInfo(wifiInfo)
+                .setIsCarrierAp(true)
+                .setCarrierName(carrierName)
+                .build();
+        assertThat(ap.getSummary()).isEqualTo(String.format(mContext.getString(
+                R.string.connected_via_carrier), carrierName));
+    }
+
+    @Test
+    public void testUpdateScanResultWithCarrierInfo() {
+        String ssid = "ssid";
+        AccessPoint ap = new TestAccessPointBuilder(mContext).setSsid(ssid).build();
+        assertThat(ap.isCarrierAp()).isEqualTo(false);
+        assertThat(ap.getCarrierApEapType()).isEqualTo(WifiEnterpriseConfig.Eap.NONE);
+        assertThat(ap.getCarrierName()).isEqualTo(null);
+
+        int carrierApEapType = WifiEnterpriseConfig.Eap.SIM;
+        String carrierName = "Test Carrier";
+        ScanResult scanResult = new ScanResult();
+        scanResult.SSID = ssid;
+        scanResult.BSSID = "00:11:22:33:44:55";
+        scanResult.capabilities = "";
+        scanResult.isCarrierAp = true;
+        scanResult.carrierApEapType = carrierApEapType;
+        scanResult.carrierName = carrierName;
+        assertThat(ap.update(scanResult)).isTrue();
+
+        assertThat(ap.isCarrierAp()).isEqualTo(true);
+        assertThat(ap.getCarrierApEapType()).isEqualTo(carrierApEapType);
+        assertThat(ap.getCarrierName()).isEqualTo(carrierName);
+    }
+
     private ScoredNetwork buildScoredNetworkWithMockBadgeCurve() {
         Bundle attr1 = new Bundle();
         attr1.putParcelable(ScoredNetwork.ATTRIBUTES_KEY_BADGING_CURVE, mockBadgeCurve);
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 6ede55d..76d9823 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -346,7 +346,10 @@
 
         Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
-        return createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(intent);
+        WifiTracker tracker =
+                createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(intent);
+        assertThat(tracker.isConnected()).isTrue();
+        return tracker;
     }
 
     private void waitForHandlersToProcessCurrentlyEnqueuedMessages(WifiTracker tracker)
@@ -860,23 +863,26 @@
         intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
         tracker.mReceiver.onReceive(mContext, intent);
 
+        waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
         verify(mockWifiListener, times(1)).onConnectedChanged();
     }
 
     @Test
-    public void onConnectedChangedCallback_shouldNBeInvokedWhenStateChanges() throws Exception {
+    public void onConnectedChangedCallback_shouldBeInvokedWhenStateChanges() throws Exception {
         WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected();
         verify(mockWifiListener, times(1)).onConnectedChanged();
 
         NetworkInfo networkInfo = new NetworkInfo(
                 ConnectivityManager.TYPE_WIFI, 0, "Type Wifi", "subtype");
         networkInfo.setDetailedState(
-                NetworkInfo.DetailedState.DISCONNECTED, "dicconnected", "test");
+                NetworkInfo.DetailedState.DISCONNECTED, "disconnected", "test");
 
         Intent intent = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
         tracker.mReceiver.onReceive(mContext, intent);
 
+        waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
+        assertThat(tracker.isConnected()).isFalse();
         verify(mockWifiListener, times(2)).onConnectedChanged();
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/android/net/wifi/WifiNetworkScoreCache.java b/packages/SettingsLib/tests/robotests/src/android/net/wifi/WifiNetworkScoreCache.java
new file mode 100644
index 0000000..abccd8d
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/android/net/wifi/WifiNetworkScoreCache.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.content.Context;
+import android.os.Handler;
+
+import java.util.List;
+
+/**
+ * Will be removed once robolectric is updated to O
+ */
+public class WifiNetworkScoreCache {
+
+    public WifiNetworkScoreCache(Context context, WifiNetworkScoreCache.CacheListener listener) {
+    }
+
+    public abstract static class CacheListener {
+        public CacheListener(Handler handler) {
+        }
+
+        void post(List updatedNetworks) {
+        }
+
+        public abstract void networkCacheUpdated(List updatedNetworks);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
new file mode 100644
index 0000000..94bfd8f
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogdSizePreferenceControllerTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.development;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.os.SystemProperties;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.R;
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
+        shadows = SystemPropertiesTestImpl.class)
+public class LogdSizePreferenceControllerTest {
+
+    @Mock
+    private ListPreference mListPreference;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+
+    private AbstractLogdSizePreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mController = new AbstractLogdSizePreferenceController(RuntimeEnvironment.application) {};
+
+        doReturn(mListPreference).when(mPreferenceScreen)
+                .findPreference(mController.getPreferenceKey());
+
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void testUpateLogdSizeValues_lowRamEntries() {
+        SystemProperties.set("ro.config.low_ram", "true");
+        mController.updateLogdSizeValues();
+        verify(mListPreference).setEntries(R.array.select_logd_size_lowram_titles);
+    }
+
+    @Test
+    public void testUpdateLogdSizeValues_silence() {
+        SystemProperties.set(
+                AbstractLogdSizePreferenceController.SELECT_LOGD_TAG_PROPERTY,
+                AbstractLogdSizePreferenceController.SELECT_LOGD_TAG_SILENCE);
+        SystemProperties.set(
+                AbstractLogdSizePreferenceController.SELECT_LOGD_SIZE_PROPERTY,
+                AbstractLogdSizePreferenceController.SELECT_LOGD_DEFAULT_SIZE_VALUE);
+        mController.updateLogdSizeValues();
+        verify(mListPreference).setValue(
+                AbstractLogdSizePreferenceController.SELECT_LOGD_OFF_SIZE_MARKER_VALUE);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
new file mode 100644
index 0000000..e133cbd
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/LogpersistPreferenceControllerTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.development;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+
+import android.os.SystemProperties;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.PreferenceScreen;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+import com.android.settingslib.TestConfig;
+import com.android.settingslib.core.lifecycle.Lifecycle;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION,
+        shadows = SystemPropertiesTestImpl.class)
+public class LogpersistPreferenceControllerTest {
+
+    private Lifecycle mLifecycle = new Lifecycle();
+
+    @Mock
+    private ListPreference mListPreference;
+    @Mock
+    private PreferenceScreen mPreferenceScreen;
+
+    private AbstractLogpersistPreferenceController mController;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        SystemProperties.set("ro.debuggable", "1");
+        mController = new AbstractLogpersistPreferenceController(RuntimeEnvironment.application,
+                mLifecycle) {
+            @Override
+            public void showConfirmationDialog(ListPreference preference) {
+
+            }
+        };
+
+        doReturn(mListPreference).when(mPreferenceScreen)
+                .findPreference(mController.getPreferenceKey());
+
+        mController.displayPreference(mPreferenceScreen);
+    }
+
+    @Test
+    public void testAvailable() {
+        SystemProperties.set("ro.debuggable", "");
+        assertThat(mController.isAvailable()).isFalse();
+        SystemProperties.set("ro.debuggable", "1");
+        assertThat(mController.isAvailable()).isTrue();
+        SystemProperties.set("ro.debuggable", "0");
+        assertThat(mController.isAvailable()).isFalse();
+    }
+
+    @Test
+    public void testUpdateLogpersistValues_null() {
+        SystemProperties.set(
+                AbstractLogpersistPreferenceController.ACTUAL_LOGPERSIST_PROPERTY,
+                AbstractLogpersistPreferenceController.SELECT_LOGPERSIST_PROPERTY_SERVICE);
+        mController.updateLogpersistValues();
+        verify(mListPreference, atLeastOnce()).setValue("all");
+    }
+
+    @Test
+    public void testUpdateLogpersistValues_all() {
+        SystemProperties.set(
+                AbstractLogpersistPreferenceController.ACTUAL_LOGPERSIST_PROPERTY,
+                AbstractLogpersistPreferenceController.SELECT_LOGPERSIST_PROPERTY_SERVICE);
+        SystemProperties.set(
+                AbstractLogpersistPreferenceController.ACTUAL_LOGPERSIST_PROPERTY_BUFFER,
+                "all");
+        mController.updateLogpersistValues();
+        verify(mListPreference, atLeastOnce()).setValue("all");
+    }
+
+    @Test
+    public void testUpdateLogpersistValues_defaultSecurityKernel() {
+        SystemProperties.set(
+                AbstractLogpersistPreferenceController.ACTUAL_LOGPERSIST_PROPERTY,
+                AbstractLogpersistPreferenceController.SELECT_LOGPERSIST_PROPERTY_SERVICE);
+        SystemProperties.set(
+                AbstractLogpersistPreferenceController.ACTUAL_LOGPERSIST_PROPERTY_BUFFER,
+                "default,security,kernel");
+        mController.updateLogpersistValues();
+        verify(mListPreference, atLeastOnce()).setValue("default,security,kernel");
+    }
+
+    @Test
+    public void testUpdateLogpersistValues_kernel() {
+        SystemProperties.set(
+                AbstractLogpersistPreferenceController.ACTUAL_LOGPERSIST_PROPERTY,
+                AbstractLogpersistPreferenceController.SELECT_LOGPERSIST_PROPERTY_SERVICE);
+        SystemProperties.set(
+                AbstractLogpersistPreferenceController.ACTUAL_LOGPERSIST_PROPERTY_BUFFER,
+                "kernel");
+        mController.updateLogpersistValues();
+        verify(mListPreference, atLeastOnce()).setValue("kernel");
+    }
+
+    @Test
+    public void testUpdateLogpersistValues_mainSecuritykernel() {
+        SystemProperties.set(
+                AbstractLogpersistPreferenceController.ACTUAL_LOGPERSIST_PROPERTY,
+                AbstractLogpersistPreferenceController.SELECT_LOGPERSIST_PROPERTY_SERVICE);
+        SystemProperties.set(
+                AbstractLogpersistPreferenceController.ACTUAL_LOGPERSIST_PROPERTY_BUFFER,
+                "main,security,kernel");
+        mController.updateLogpersistValues();
+        verify(mListPreference, atLeastOnce()).setValue("all");
+    }
+}
+
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropertiesTestImpl.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropertiesTestImpl.java
new file mode 100644
index 0000000..2f89d86
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/development/SystemPropertiesTestImpl.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.development;
+
+import android.text.TextUtils;
+import android.util.ArrayMap;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadows.ShadowSystemProperties;
+
+import java.util.Map;
+
+@Implements(className = "android.os.SystemProperties")
+public class SystemPropertiesTestImpl extends ShadowSystemProperties {
+
+    private static Map<String, String> sProperties = new ArrayMap<>();
+
+    @Implementation
+    public static String get(String key) {
+        String value = sProperties.get(key);
+        if (!TextUtils.isEmpty(value)) {
+            return value;
+        } else {
+            return ShadowSystemProperties.get(key);
+        }
+    }
+
+    @Implementation
+    public static String get(String key, String def) {
+        String value = sProperties.get(key);
+        if (!TextUtils.isEmpty(value)) {
+            return value;
+        } else {
+            return ShadowSystemProperties.get(key, def);
+        }
+    }
+
+    @Implementation
+    public static void set(String key, String val) {
+        sProperties.put(key, val);
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
index e9ca753..3e90435 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/drawer/TileUtilsTest.java
@@ -535,14 +535,10 @@
             info.activityInfo.metaData.putString("com.android.settings.summary_uri", summaryUri);
         }
         if (titleResId != 0) {
-            info.activityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_TITLE, title);
+            info.activityInfo.metaData.putInt(TileUtils.META_DATA_PREFERENCE_TITLE, titleResId);
         } else if (title != null) {
             info.activityInfo.metaData.putString(TileUtils.META_DATA_PREFERENCE_TITLE, title);
         }
-        if (titleResId != 0) {
-            info.activityInfo.metaData.putInt(
-                    TileUtils.META_DATA_PREFERENCE_TITLE_RES_ID, titleResId);
-        }
         info.activityInfo.applicationInfo = new ApplicationInfo();
         if (systemApp) {
             info.activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index 96f51c1..3d0147d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -34,6 +34,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.util.ArrayMap;
 import android.util.BackupUtils;
 import android.util.Log;
 
@@ -62,6 +63,9 @@
     private static final boolean DEBUG = false;
     private static final boolean DEBUG_BACKUP = DEBUG || false;
 
+    private static final byte[] NULL_VALUE = new byte[0];
+    private static final int NULL_SIZE = -1;
+
     private static final String KEY_SYSTEM = "system";
     private static final String KEY_SECURE = "secure";
     private static final String KEY_GLOBAL = "global";
@@ -608,7 +612,7 @@
 
         // Restore only the white list data.
         int pos = 0;
-        Map<String, String> cachedEntries = new HashMap<String, String>();
+        final ArrayMap<String, String> cachedEntries = new ArrayMap<>();
         ContentValues contentValues = new ContentValues(2);
         SettingsHelper settingsHelper = mSettingsHelper;
         ContentResolver cr = getContentResolver();
@@ -616,28 +620,36 @@
         final int whiteListSize = whitelist.length;
         for (int i = 0; i < whiteListSize; i++) {
             String key = whitelist[i];
-            String value = cachedEntries.remove(key);
 
-            // If the value not cached, let us look it up.
-            if (value == null) {
+            String value = null;
+            boolean hasValueToRestore = false;
+            if (cachedEntries.indexOfKey(key) >= 0) {
+                value = cachedEntries.remove(key);
+                hasValueToRestore = true;
+            } else {
+                // If the value not cached, let us look it up.
                 while (pos < bytes) {
                     int length = readInt(settings, pos);
                     pos += INTEGER_BYTE_COUNT;
-                    String dataKey = length > 0 ? new String(settings, pos, length) : null;
+                    String dataKey = length >= 0 ? new String(settings, pos, length) : null;
                     pos += length;
                     length = readInt(settings, pos);
                     pos += INTEGER_BYTE_COUNT;
-                    String dataValue = length > 0 ? new String(settings, pos, length) : null;
-                    pos += length;
+                    String dataValue = null;
+                    if (length >= 0) {
+                        dataValue = new String(settings, pos, length);
+                        pos += length;
+                    }
                     if (key.equals(dataKey)) {
                         value = dataValue;
+                        hasValueToRestore = true;
                         break;
                     }
                     cachedEntries.put(dataKey, dataValue);
                 }
             }
 
-            if (value == null) {
+            if (!hasValueToRestore) {
                 continue;
             }
 
@@ -724,50 +736,56 @@
      * @return The byte array of extracted values.
      */
     private byte[] extractRelevantValues(Cursor cursor, String[] settings) {
-        final int settingsCount = settings.length;
-        byte[][] values = new byte[settingsCount * 2][]; // keys and values
         if (!cursor.moveToFirst()) {
             Log.e(TAG, "Couldn't read from the cursor");
             return new byte[0];
         }
 
+        final int nameColumnIndex = cursor.getColumnIndex(Settings.NameValueTable.NAME);
+        final int valueColumnIndex = cursor.getColumnIndex(Settings.NameValueTable.VALUE);
+
         // Obtain the relevant data in a temporary array.
         int totalSize = 0;
         int backedUpSettingIndex = 0;
-        Map<String, String> cachedEntries = new HashMap<String, String>();
+        final int settingsCount = settings.length;
+        final byte[][] values = new byte[settingsCount * 2][]; // keys and values
+        final ArrayMap<String, String> cachedEntries = new ArrayMap<>();
         for (int i = 0; i < settingsCount; i++) {
-            String key = settings[i];
-            String value = cachedEntries.remove(key);
-
-            final int nameColumnIndex = cursor.getColumnIndex(Settings.NameValueTable.NAME);
-            final int valueColumnIndex = cursor.getColumnIndex(Settings.NameValueTable.VALUE);
+            final String key = settings[i];
 
             // If the value not cached, let us look it up.
-            if (value == null) {
+            String value = null;
+            boolean hasValueToBackup = false;
+            if (cachedEntries.indexOfKey(key) >= 0) {
+                value = cachedEntries.remove(key);
+                hasValueToBackup = true;
+            } else {
                 while (!cursor.isAfterLast()) {
-                    String cursorKey = cursor.getString(nameColumnIndex);
-                    String cursorValue = cursor.getString(valueColumnIndex);
+                    final String cursorKey = cursor.getString(nameColumnIndex);
+                    final String cursorValue = cursor.getString(valueColumnIndex);
                     cursor.moveToNext();
                     if (key.equals(cursorKey)) {
                         value = cursorValue;
+                        hasValueToBackup = true;
                         break;
                     }
                     cachedEntries.put(cursorKey, cursorValue);
                 }
             }
 
+            if (!hasValueToBackup) {
+                continue;
+            }
+
             // Intercept the keys and see if they need special handling
             value = mSettingsHelper.onBackupValue(key, value);
 
-            if (value == null) {
-                continue;
-            }
             // Write the key and value in the intermediary array.
-            byte[] keyBytes = key.getBytes();
+            final byte[] keyBytes = key.getBytes();
             totalSize += INTEGER_BYTE_COUNT + keyBytes.length;
             values[backedUpSettingIndex * 2] = keyBytes;
 
-            byte[] valueBytes = value.getBytes();
+            final byte[] valueBytes = (value != null) ? value.getBytes() : NULL_VALUE;
             totalSize += INTEGER_BYTE_COUNT + valueBytes.length;
             values[backedUpSettingIndex * 2 + 1] = valueBytes;
 
@@ -783,8 +801,13 @@
         int pos = 0;
         final int keyValuePairCount = backedUpSettingIndex * 2;
         for (int i = 0; i < keyValuePairCount; i++) {
-            pos = writeInt(result, pos, values[i].length);
-            pos = writeBytes(result, pos, values[i]);
+            final byte[] value = values[i];
+            if (value != NULL_VALUE) {
+                pos = writeInt(result, pos, value.length);
+                pos = writeBytes(result, pos, value);
+            } else {
+                pos = writeInt(result, pos, NULL_SIZE);
+            }
         }
         return result;
     }
diff --git a/packages/SystemUI/res-keyguard/drawable-hdpi/ic_done_wht.png b/packages/SystemUI/res-keyguard/drawable-hdpi/ic_done_wht.png
deleted file mode 100644
index 82c01ef..0000000
--- a/packages/SystemUI/res-keyguard/drawable-hdpi/ic_done_wht.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-mdpi/ic_done_wht.png b/packages/SystemUI/res-keyguard/drawable-mdpi/ic_done_wht.png
deleted file mode 100644
index 8c16930..0000000
--- a/packages/SystemUI/res-keyguard/drawable-mdpi/ic_done_wht.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xhdpi/ic_done_wht.png b/packages/SystemUI/res-keyguard/drawable-xhdpi/ic_done_wht.png
deleted file mode 100644
index 6a4d8a7..0000000
--- a/packages/SystemUI/res-keyguard/drawable-xhdpi/ic_done_wht.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxhdpi/ic_done_wht.png b/packages/SystemUI/res-keyguard/drawable-xxhdpi/ic_done_wht.png
deleted file mode 100644
index 4c04ba2..0000000
--- a/packages/SystemUI/res-keyguard/drawable-xxhdpi/ic_done_wht.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/ic_done_wht.png b/packages/SystemUI/res-keyguard/drawable-xxxhdpi/ic_done_wht.png
deleted file mode 100644
index bd6c4df..0000000
--- a/packages/SystemUI/res-keyguard/drawable-xxxhdpi/ic_done_wht.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_backspace_24dp.xml b/packages/SystemUI/res-keyguard/drawable/ic_backspace_24dp.xml
deleted file mode 100644
index 1e4022e..0000000
--- a/packages/SystemUI/res-keyguard/drawable/ic_backspace_24dp.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-Copyright (C) 2014 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-        android:width="24dp"
-        android:autoMirrored="true"
-        android:height="24dp"
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0">
-
-    <path
-        android:fillColor="#ffffffff"
-        android:pathData="M44.0,6.0L14.0,6.0c-1.4,0.0 -2.5,0.7 -3.2,1.8L0.0,24.0l10.8,16.2c0.7,1.1 1.8,1.8 3.2,1.8l30.0,0.0c2.2,0.0 4.0,-1.8 4.0,-4.0L48.0,10.0C48.0,7.8 46.2,6.0 44.0,6.0zM38.0,31.2L35.2,34.0L28.0,26.8L20.8,34.0L18.0,31.2l7.2,-7.2L18.0,16.8l2.8,-2.8l7.2,7.2l7.2,-7.2l2.8,2.8L30.8,24.0L38.0,31.2z"/>
-</vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_backspace_black_24dp.xml b/packages/SystemUI/res-keyguard/drawable/ic_backspace_black_24dp.xml
new file mode 100644
index 0000000..6edae4b
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ic_backspace_black_24dp.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,3H7C6.31,3 5.77,3.35 5.41,3.88l-5.04,7.57c-0.22,0.34 -0.22,0.77 0,1.11l5.04,7.56C5.77,20.64 6.31,21 7,21h15c1.1,0 2,-0.9 2,-2V5C24,3.9 23.1,3 22,3zM18.3,16.3L18.3,16.3c-0.39,0.39 -1.02,0.39 -1.41,0L14,13.41l-2.89,2.89c-0.39,0.39 -1.02,0.39 -1.41,0h0c-0.39,-0.39 -0.39,-1.02 0,-1.41L12.59,12L9.7,9.11c-0.39,-0.39 -0.39,-1.02 0,-1.41l0,0c0.39,-0.39 1.02,-0.39 1.41,0L14,10.59l2.89,-2.89c0.39,-0.39 1.02,-0.39 1.41,0v0c0.39,0.39 0.39,1.02 0,1.41L15.41,12l2.89,2.89C18.68,15.27 18.68,15.91 18.3,16.3z"/>
+</vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_done_black_24dp.xml b/packages/SystemUI/res-keyguard/drawable/ic_done_black_24dp.xml
new file mode 100644
index 0000000..5026f07
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ic_done_black_24dp.xml
@@ -0,0 +1,25 @@
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M9,16.2l-3.5,-3.5a0.984,0.984 0,0 0,-1.4 0,0.984 0.984,0 0,0 0,1.4l4.19,4.19c0.39,0.39 1.02,0.39 1.41,0L20.3,7.7a0.984,0.984 0,0 0,0 -1.4,0.984 0.984,0 0,0 -1.4,0L9,16.2z"/>
+</vector>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
index 3283e04..631cc0d 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_pin_view.xml
@@ -62,7 +62,7 @@
                     android:layout_width="wrap_content"
                     android:layout_height="match_parent"
                     android:gravity="center_vertical"
-                    android:src="@drawable/ic_backspace_24dp"
+                    android:src="@drawable/ic_backspace_black_24dp"
                     android:clickable="true"
                     android:paddingTop="8dip"
                     android:paddingBottom="8dip"
@@ -73,6 +73,7 @@
                     android:layout_alignEnd="@+id/pinEntry"
                     android:layout_alignParentRight="true"
                     android:tint="@color/pin_delete_color"
+                    android:tintMode="src_in"
                     />
             <View
                     android:id="@+id/divider"
@@ -204,7 +205,7 @@
                     android:layout_height="match_parent"
                     android:layout_weight="1"
                     android:paddingBottom="11sp"
-                    android:src="@drawable/ic_done_wht"
+                    android:src="@drawable/ic_done_black_24dp"
                     style="@style/Keyguard.ImageButton.NumPadEnter"
                     android:background="@drawable/ripple_drawable"
                     android:contentDescription="@string/keyboardview_keycode_enter"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
index cf87f90..97c8965 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
@@ -75,7 +75,7 @@
                     android:layout_width="wrap_content"
                     android:layout_height="match_parent"
                     android:gravity="center_vertical"
-                    android:src="@drawable/ic_backspace_24dp"
+                    android:src="@drawable/ic_backspace_black_24dp"
                     android:clickable="true"
                     android:paddingTop="8dip"
                     android:paddingBottom="8dip"
@@ -86,6 +86,7 @@
                     android:layout_alignEnd="@+id/pinEntry"
                     android:layout_alignParentRight="true"
                     android:tint="@color/pin_delete_color"
+                    android:tintMode="src_in"
                     />
             <View
                     android:id="@+id/divider"
@@ -213,7 +214,7 @@
                     android:layout_height="match_parent"
                     android:layout_weight="1"
                     android:paddingBottom="11sp"
-                    android:src="@drawable/ic_done_wht"
+                    android:src="@drawable/ic_done_black_24dp"
                     style="@style/Keyguard.ImageButton.NumPadEnter"
                     android:background="@drawable/ripple_drawable"
                     android:contentDescription="@string/keyboardview_keycode_enter"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
index 3cae493..d4c5d74 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
@@ -76,7 +76,7 @@
                     android:layout_width="wrap_content"
                     android:layout_height="match_parent"
                     android:gravity="center_vertical"
-                    android:src="@drawable/ic_backspace_24dp"
+                    android:src="@drawable/ic_backspace_black_24dp"
                     android:clickable="true"
                     android:paddingTop="8dip"
                     android:paddingBottom="8dip"
@@ -87,6 +87,7 @@
                     android:layout_alignEnd="@+id/pinEntry"
                     android:layout_alignParentRight="true"
                     android:tint="@color/pin_delete_color"
+                    android:tintMode="src_in"
                     />
             <View
                     android:id="@+id/divider"
@@ -214,7 +215,7 @@
                     android:layout_height="match_parent"
                     android:layout_weight="1"
                     android:paddingBottom="11sp"
-                    android:src="@drawable/ic_done_wht"
+                    android:src="@drawable/ic_done_black_24dp"
                     style="@style/Keyguard.ImageButton.NumPadEnter"
                     android:background="@drawable/ripple_drawable"
                     android:contentDescription="@string/keyboardview_keycode_enter"
diff --git a/packages/SystemUI/res/drawable/stat_sys_managed_profile_status.xml b/packages/SystemUI/res/drawable/stat_sys_managed_profile_status.xml
index 27caf96..e38b482 100644
--- a/packages/SystemUI/res/drawable/stat_sys_managed_profile_status.xml
+++ b/packages/SystemUI/res/drawable/stat_sys_managed_profile_status.xml
@@ -8,5 +8,5 @@
    -->
     <path
         android:pathData="M17.32,5.06h-2.91V3.6c0,-0.81 -0.65,-1.46 -1.46,-1.46h-2.91c-0.81,0 -1.46,0.65 -1.46,1.46v1.46H5.68c-0.81,0 -1.45,0.65 -1.45,1.46l-0.01,8c0,0.81 0.65,1.46 1.46,1.46h11.64c0.81,0 1.46,-0.65 1.46,-1.46v-8C18.78,5.7 18.13,5.06 17.32,5.06zM11.5,11.6c-0.8,0 -1.46,-0.65 -1.46,-1.46c0,-0.8 0.65,-1.46 1.46,-1.46s1.46,0.65 1.46,1.46C12.96,10.95 12.3,11.6 11.5,11.6zM12.96,5.06h-2.91V3.6h2.91V5.06z"
-        android:fillColor="#FFFFFF"/>
-</vector>
\ No newline at end of file
+        android:fillColor="#FF000000"/>
+</vector>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 054d520..f72f379 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -116,11 +116,11 @@
     <color name="segmented_buttons_background">#14FFFFFF</color><!-- 8% white -->
 
     <color name="dark_mode_icon_color_single_tone">#99000000</color>
-    <color name="dark_mode_icon_color_dual_tone_background">#4d000000</color>
+    <color name="dark_mode_icon_color_dual_tone_background">#3d000000</color>
     <color name="dark_mode_icon_color_dual_tone_fill">#7a000000</color>
 
     <color name="light_mode_icon_color_single_tone">#ffffff</color>
-    <color name="light_mode_icon_color_dual_tone_background">#5dffffff</color>
+    <color name="light_mode_icon_color_dual_tone_background">#4dffffff</color>
     <color name="light_mode_icon_color_dual_tone_fill">#ffffff</color>
 
     <color name="volume_settings_icon_color">#7fffffff</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 5917dc5..506c294 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -277,6 +277,18 @@
         <item>28</item> <!-- 4: SUN -->
     </integer-array>
 
+    <!-- Doze: Table that translates sensor values from the doze_brightness_sensor_type sensor
+               to an opacity value for a black scrim that is overlayed in AOD1.
+               Valid range is from 0 (transparent) to 255 (opaque).
+               -1 means keeping the current opacity. -->
+    <integer-array name="config_doze_brightness_sensor_to_scrim_opacity">
+        <item>-1</item> <!-- 0: OFF -->
+        <item>0</item> <!-- 1: NIGHT -->
+        <item>0</item> <!-- 2: LOW -->
+        <item>0</item> <!-- 3: HIGH -->
+        <item>0</item> <!-- 4: SUN -->
+    </integer-array>
+
     <!-- Doze: whether the double tap sensor reports 2D touch coordinates -->
     <bool name="doze_double_tap_reports_touch_coordinates">false</bool>
 
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index e5c729f..9c1cb4e 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -16,6 +16,7 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
+import android.hardware.SensorManager;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
@@ -84,6 +85,7 @@
 import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerServiceImpl;
+import com.android.systemui.util.AsyncSensorManager;
 import com.android.systemui.util.leak.GarbageMonitor;
 import com.android.systemui.util.leak.LeakDetector;
 import com.android.systemui.util.leak.LeakReporter;
@@ -156,6 +158,9 @@
         mProviders.put(ActivityStarterDelegate.class, () ->
                 getDependency(ActivityStarter.class));
 
+        mProviders.put(AsyncSensorManager.class, () ->
+                new AsyncSensorManager(mContext.getSystemService(SensorManager.class)));
+
         mProviders.put(BluetoothController.class, () ->
                 new BluetoothControllerImpl(mContext, getDependency(BG_LOOPER)));
 
diff --git a/packages/SystemUI/src/com/android/systemui/RoundedCorners.java b/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
index 40ea4ec..37e2402 100644
--- a/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
+++ b/packages/SystemUI/src/com/android/systemui/RoundedCorners.java
@@ -119,7 +119,7 @@
                         | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
                 PixelFormat.TRANSLUCENT);
         lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS
-                | WindowManager.LayoutParams.PRIVATE_FLAG_NO_MAGNIFICATION_REGION_EFFECT;
+                | WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
         lp.setTitle("RoundedOverlay");
         lp.gravity = Gravity.TOP;
         return lp;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
index e92ed2f..e4b405f 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManager.java
@@ -35,6 +35,7 @@
 import com.android.systemui.UiOffloadThread;
 import com.android.systemui.analytics.DataCollector;
 import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.util.AsyncSensorManager;
 
 import java.io.PrintWriter;
 
@@ -87,7 +88,7 @@
 
     private FalsingManager(Context context) {
         mContext = context;
-        mSensorManager = mContext.getSystemService(SensorManager.class);
+        mSensorManager = Dependency.get(AsyncSensorManager.class);
         mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
         mDataCollector = DataCollector.getInstance(mContext);
         mHumanInteractionClassifier = HumanInteractionClassifier.getInstance(mContext);
diff --git a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
index 9ba7be8..34c05a5 100644
--- a/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
+++ b/packages/SystemUI/src/com/android/systemui/colorextraction/SysuiColorExtractor.java
@@ -21,6 +21,8 @@
 import android.content.Context;
 import android.os.Handler;
 import android.os.RemoteException;
+import android.os.Trace;
+import android.os.UserHandle;
 import android.util.Log;
 import android.view.Display;
 import android.view.IWallpaperVisibilityListener;
@@ -31,6 +33,7 @@
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.colorextraction.types.ExtractionType;
 import com.android.internal.colorextraction.types.Tonal;
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.Dumpable;
 
 import java.io.FileDescriptor;
@@ -75,6 +78,14 @@
                 Log.w(TAG, "Can't listen to wallpaper visibility changes", e);
             }
         }
+
+        WallpaperManager wallpaperManager = context.getSystemService(WallpaperManager.class);
+        if (wallpaperManager != null) {
+            // Listen to all users instead of only the current one.
+            wallpaperManager.removeOnColorsChangedListener(this);
+            wallpaperManager.addOnColorsChangedListener(this, null /* handler */,
+                    UserHandle.USER_ALL);
+        }
     }
 
     private void updateDefaultGradients(WallpaperColors colors) {
@@ -82,7 +93,12 @@
     }
 
     @Override
-    public void onColorsChanged(WallpaperColors colors, int which) {
+    public void onColorsChanged(WallpaperColors colors, int which, int userId) {
+        if (userId != KeyguardUpdateMonitor.getCurrentUser()) {
+            // Colors do not belong to current user, ignoring.
+            return;
+        }
+
         super.onColorsChanged(colors, which);
 
         if ((which & WallpaperManager.FLAG_SYSTEM) != 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 91ca571..302bc2d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -24,10 +24,12 @@
 import android.os.Handler;
 
 import com.android.internal.hardware.AmbientDisplayConfiguration;
+import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SystemUIApplication;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.AsyncSensorManager;
 import com.android.systemui.util.wakelock.DelayedWakeLock;
 import com.android.systemui.util.wakelock.WakeLock;
 
@@ -39,7 +41,7 @@
     /** Creates a DozeMachine with its parts for {@code dozeService}. */
     public DozeMachine assembleMachine(DozeService dozeService) {
         Context context = dozeService;
-        SensorManager sensorManager = context.getSystemService(SensorManager.class);
+        SensorManager sensorManager = Dependency.get(AsyncSensorManager.class);
         AlarmManager alarmManager = context.getSystemService(AlarmManager.class);
 
         DozeHost host = getHost(dozeService);
@@ -63,7 +65,7 @@
                         handler, wakeLock, machine),
                 createDozeUi(context, host, wakeLock, machine, handler, alarmManager),
                 createDozeScreenState(wrappedService),
-                createDozeScreenBrightness(context, wrappedService, sensorManager, handler),
+                createDozeScreenBrightness(context, wrappedService, sensorManager, host, handler),
         });
 
         return machine;
@@ -74,10 +76,11 @@
     }
 
     private DozeMachine.Part createDozeScreenBrightness(Context context,
-            DozeMachine.Service service, SensorManager sensorManager, Handler handler) {
+            DozeMachine.Service service, SensorManager sensorManager, DozeHost host,
+            Handler handler) {
         Sensor sensor = DozeSensors.findSensorWithType(sensorManager,
                 context.getString(R.string.doze_brightness_sensor_type));
-        return new DozeScreenBrightness(context, service, sensorManager, sensor, handler);
+        return new DozeScreenBrightness(context, service, sensorManager, sensor, host, handler);
     }
 
     private DozeTriggers createDozeTriggers(Context context, SensorManager sensorManager,
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 9b97634..7db118d 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -42,6 +42,7 @@
 
     void onDoubleTap(float x, float y);
 
+    default void setAodDimmingScrim(float scrimOpacity) {}
     void setDozeScreenBrightness(int value);
 
     void onIgnoreTouchWhilePulsing(boolean ignore);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
index 32baf94..30420529 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenBrightness.java
@@ -32,22 +32,28 @@
 public class DozeScreenBrightness implements DozeMachine.Part, SensorEventListener {
     private final Context mContext;
     private final DozeMachine.Service mDozeService;
+    private final DozeHost mDozeHost;
     private final Handler mHandler;
     private final SensorManager mSensorManager;
     private final Sensor mLightSensor;
     private final int[] mSensorToBrightness;
+    private final int[] mSensorToScrimOpacity;
     private boolean mRegistered;
 
     public DozeScreenBrightness(Context context, DozeMachine.Service service,
-            SensorManager sensorManager, Sensor lightSensor, Handler handler) {
+            SensorManager sensorManager, Sensor lightSensor, DozeHost host,
+            Handler handler) {
         mContext = context;
         mDozeService = service;
         mSensorManager = sensorManager;
         mLightSensor = lightSensor;
+        mDozeHost = host;
         mHandler = handler;
 
         mSensorToBrightness = context.getResources().getIntArray(
                 R.array.config_doze_brightness_sensor_to_brightness);
+        mSensorToScrimOpacity = context.getResources().getIntArray(
+                R.array.config_doze_brightness_sensor_to_scrim_opacity);
     }
 
     @Override
@@ -74,13 +80,26 @@
     @Override
     public void onSensorChanged(SensorEvent event) {
         if (mRegistered) {
-            int brightness = computeBrightness((int) event.values[0]);
+            int sensorValue = (int) event.values[0];
+            int brightness = computeBrightness(sensorValue);
             if (brightness > 0) {
                 mDozeService.setDozeScreenBrightness(brightness);
             }
+
+            int scrimOpacity = computeScrimOpacity(sensorValue);
+            if (scrimOpacity >= 0) {
+                mDozeHost.setAodDimmingScrim(scrimOpacity / 255f);
+            }
         }
     }
 
+    private int computeScrimOpacity(int sensorValue) {
+        if (sensorValue < 0 || sensorValue >= mSensorToScrimOpacity.length) {
+            return -1;
+        }
+        return mSensorToScrimOpacity[sensorValue];
+    }
+
     private int computeBrightness(int sensorValue) {
         if (sensorValue < 0 || sensorValue >= mSensorToBrightness.length) {
             return -1;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index bf1c060..566353c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -87,25 +87,29 @@
                         mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION),
                         null /* setting */,
                         dozeParameters.getPulseOnSigMotion(),
-                        DozeLog.PULSE_REASON_SENSOR_SIGMOTION, false /* touchCoords */),
+                        DozeLog.PULSE_REASON_SENSOR_SIGMOTION, false /* touchCoords */,
+                        false /* touchscreen */),
                 mPickupSensor = new TriggerSensor(
                         mSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE),
                         Settings.Secure.DOZE_PULSE_ON_PICK_UP,
                         config.pulseOnPickupAvailable(),
-                        DozeLog.PULSE_REASON_SENSOR_PICKUP, false /* touchCoords */),
+                        DozeLog.PULSE_REASON_SENSOR_PICKUP, false /* touchCoords */,
+                        false /* touchscreen */),
                 new TriggerSensor(
                         findSensorWithType(config.doubleTapSensorType()),
                         Settings.Secure.DOZE_PULSE_ON_DOUBLE_TAP,
                         true /* configured */,
                         DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP,
-                        dozeParameters.doubleTapReportsTouchCoordinates()),
+                        dozeParameters.doubleTapReportsTouchCoordinates(),
+                        true /* touchscreen */),
                 new TriggerSensor(
                         findSensorWithType(config.longPressSensorType()),
                         Settings.Secure.DOZE_PULSE_ON_LONG_PRESS,
                         false /* settingDef */,
                         true /* configured */,
                         DozeLog.PULSE_REASON_SENSOR_LONG_PRESS,
-                        true /* reports touch coordinates */),
+                        true /* reports touch coordinates */,
+                        true /* touchscreen */),
         };
 
         mProxSensor = new ProxSensor();
@@ -141,6 +145,15 @@
         }
     }
 
+    /** Set the listening state of only the sensors that require the touchscreen. */
+    public void setTouchscreenSensorsListening(boolean listening) {
+        for (TriggerSensor sensor : mSensors) {
+            if (sensor.mRequiresTouchscreen) {
+                sensor.setListening(listening);
+            }
+        }
+    }
+
     public void reregisterAllSensors() {
         for (TriggerSensor s : mSensors) {
             s.setListening(false);
@@ -278,25 +291,28 @@
         final String mSetting;
         final boolean mReportsTouchCoordinates;
         final boolean mSettingDefault;
+        final boolean mRequiresTouchscreen;
 
         private boolean mRequested;
         private boolean mRegistered;
         private boolean mDisabled;
 
         public TriggerSensor(Sensor sensor, String setting, boolean configured, int pulseReason,
-                boolean reportsTouchCoordinates) {
+                boolean reportsTouchCoordinates, boolean requiresTouchscreen) {
             this(sensor, setting, true /* settingDef */, configured, pulseReason,
-                    reportsTouchCoordinates);
+                    reportsTouchCoordinates, requiresTouchscreen);
         }
 
         public TriggerSensor(Sensor sensor, String setting, boolean settingDef,
-                boolean configured, int pulseReason, boolean reportsTouchCoordinates) {
+                boolean configured, int pulseReason, boolean reportsTouchCoordinates,
+                boolean requiresTouchscreen) {
             mSensor = sensor;
             mSetting = setting;
             mSettingDefault = settingDef;
             mConfigured = configured;
             mPulseReason = pulseReason;
             mReportsTouchCoordinates = reportsTouchCoordinates;
+            mRequiresTouchscreen = requiresTouchscreen;
         }
 
         public void setListening(boolean listen) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 15981e5..4583160 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -198,6 +198,7 @@
                 mDozeSensors.setListening(false);
                 break;
             case DOZE_PULSING:
+                mDozeSensors.setTouchscreenSensorsListening(false);
                 mDozeSensors.setProxListening(true);
                 break;
             case FINISH:
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 8847452..dc626fb 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -95,17 +95,22 @@
                 unscheduleTimeTick();
                 break;
         }
-        mHost.setAnimateWakeup(shouldAnimateWakeup(newState));
+        updateAnimateWakeup(newState);
     }
 
-    private boolean shouldAnimateWakeup(DozeMachine.State state) {
+    private void updateAnimateWakeup(DozeMachine.State state) {
         switch (state) {
             case DOZE_REQUEST_PULSE:
             case DOZE_PULSING:
             case DOZE_PULSE_DONE:
-                return true;
+                mHost.setAnimateWakeup(true);
+                break;
+            case FINISH:
+                // Keep current state.
+                break;
             default:
-                return false;
+                mHost.setAnimateWakeup(false);
+                break;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
index 6733421..abc5667 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/InputConsumerController.java
@@ -65,7 +65,7 @@
         }
 
         @Override
-        public void onInputEvent(InputEvent event) {
+        public void onInputEvent(InputEvent event, int displayId) {
             boolean handled = true;
             try {
                 // To be implemented for input handling over Pip windows
diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
index 1240e05..cc7798e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
+++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl
@@ -31,4 +31,5 @@
     void sendRecentsDrawnEvent();
     void sendDockingTopTaskEvent(int dragMode, in Rect initialRect);
     void sendLaunchRecentsEvent();
+    void setWaitingForTransitionStartEvent(boolean waitingForTransitionStart);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index a5c994e..d10e080 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -54,9 +54,11 @@
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
 import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
+import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent;
 import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
 import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent;
 import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
 import com.android.systemui.recents.events.component.ShowUserToastEvent;
 import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
@@ -612,6 +614,14 @@
                 }
             });
         }
+
+        // This will catch the cases when a user launches from recents to another app
+        // (and vice versa) that is not in the recents stack (such as home or bugreport) and it
+        // would not reset the wait for transition flag. This will catch it and make sure that the
+        // flag is reset.
+        if (!event.visible) {
+            mImpl.setWaitingForTransitionStart(false);
+        }
     }
 
     /**
@@ -684,6 +694,11 @@
         }
     }
 
+    public final void onBusEvent(LaunchTaskFailedEvent event) {
+        // Reset the transition when tasks fail to launch
+        mImpl.setWaitingForTransitionStart(false);
+    }
+
     public final void onBusEvent(ConfigurationChangedEvent event) {
         // Update the configuration for the Recents component when the activity configuration
         // changes as well
@@ -711,6 +726,25 @@
         }
     }
 
+    public final void onBusEvent(SetWaitingForTransitionStartEvent event) {
+        int processUser = sSystemServicesProxy.getProcessUser();
+        if (sSystemServicesProxy.isSystemUser(processUser)) {
+            mImpl.setWaitingForTransitionStart(event.waitingForTransitionStart);
+        } else {
+            postToSystemUser(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        mUserToSystemCallbacks.setWaitingForTransitionStartEvent(
+                                event.waitingForTransitionStart);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Callback failed", e);
+                    }
+                }
+            });
+        }
+    }
+
     /**
      * Attempts to register with the system user.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 7cd93bb..86e93fd 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -24,12 +24,11 @@
 import android.app.ActivityManager;
 import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityOptions;
+import android.app.ActivityOptions.OnAnimationStartedListener;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
 import android.graphics.RectF;
@@ -208,6 +207,20 @@
     protected static RecentsTaskLoadPlan sInstanceLoadPlan;
     // Stores the last pinned task time
     protected static long sLastPipTime = -1;
+    // Stores whether we are waiting for a transition to/from recents to start. During this time,
+    // we disallow the user from manually toggling recents until the transition has started.
+    private static boolean mWaitingForTransitionStart = false;
+    // Stores whether or not the user toggled while we were waiting for a transition to/from
+    // recents. In this case, we defer the toggle state until then and apply it immediately after.
+    private static boolean mToggleFollowingTransitionStart = true;
+
+    private ActivityOptions.OnAnimationStartedListener mResetToggleFlagListener =
+            new OnAnimationStartedListener() {
+                @Override
+                public void onAnimationStarted() {
+                    setWaitingForTransitionStart(false);
+                }
+            };
 
     protected Context mContext;
     protected Handler mHandler;
@@ -365,6 +378,11 @@
             return;
         }
 
+        if (mWaitingForTransitionStart) {
+            mToggleFollowingTransitionStart = true;
+            return;
+        }
+
         mDraggingInRecents = false;
         mLaunchedWhileDocking = false;
         mTriggeredFromAltTab = false;
@@ -638,6 +656,18 @@
         }
     }
 
+    public void setWaitingForTransitionStart(boolean waitingForTransitionStart) {
+        if (mWaitingForTransitionStart == waitingForTransitionStart) {
+            return;
+        }
+
+        mWaitingForTransitionStart = waitingForTransitionStart;
+        if (!waitingForTransitionStart && mToggleFollowingTransitionStart) {
+            toggleRecents(DividerView.INVALID_RECENTS_GROW_TARGET);
+        }
+        mToggleFollowingTransitionStart = false;
+    }
+
     /**
      * Returns the preloaded load plan and invalidates it.
      */
@@ -865,8 +895,9 @@
             }
             AppTransitionAnimationSpec[] specsArray = new AppTransitionAnimationSpec[specs.size()];
             specs.toArray(specsArray);
+
             return new Pair<>(ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView,
-                    specsArray, mHandler, null, this), null);
+                    specsArray, mHandler, mResetToggleFlagListener, this), null);
         } else {
             // Update the destination rect
             Task toTask = new Task();
@@ -884,8 +915,10 @@
                         return Lists.newArrayList(new AppTransitionAnimationSpec(
                                 toTask.key.id, thumbnail, rect));
                     });
+
             return new Pair<>(ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(mContext,
-                    mHandler, future.getFuture(), null, false /* scaleUp */), future);
+                    mHandler, future.getFuture(), mResetToggleFlagListener, false /* scaleUp */),
+                    future);
         }
     }
 
@@ -991,6 +1024,10 @@
         launchState.launchedToTaskId = runningTaskId;
         launchState.launchedWithAltTab = mTriggeredFromAltTab;
 
+        // Disable toggling of recents between starting the activity and it is visible and the app
+        // has started its transition into recents.
+        setWaitingForTransitionStart(useThumbnailTransition);
+
         // Preload the icon (this will be a null-op if we have preloaded the icon already in
         // preloadRecents())
         preloadIcon(runningTaskId);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
index 3921a20..1285626 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java
@@ -29,6 +29,7 @@
 import com.android.systemui.recents.events.EventBus;
 import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
 import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent;
+import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
 import com.android.systemui.recents.events.ui.RecentsDrawnEvent;
 import com.android.systemui.recents.misc.ForegroundThread;
 
@@ -105,4 +106,10 @@
     public void sendLaunchRecentsEvent() throws RemoteException {
         EventBus.getDefault().post(new RecentsActivityStartingEvent());
     }
+
+    @Override
+    public void setWaitingForTransitionStartEvent(boolean waitingForTransitionStart) {
+        EventBus.getDefault().post(new SetWaitingForTransitionStartEvent(
+                waitingForTransitionStart));
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java
new file mode 100644
index 0000000..d9cf5fb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.recents.events.component;
+
+import com.android.systemui.recents.events.EventBus;
+
+/**
+ * This is sent when we are setting/resetting the flag to wait for the transition to start.
+ */
+public class SetWaitingForTransitionStartEvent extends EventBus.Event {
+
+    public final boolean waitingForTransitionStart;
+
+    public SetWaitingForTransitionStartEvent(boolean waitingForTransitionStart) {
+        this.waitingForTransitionStart = waitingForTransitionStart;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index ce40f20..127822a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -54,6 +54,7 @@
 import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent;
 import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent;
 import com.android.systemui.recents.events.component.ScreenPinningRequestEvent;
+import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.recents.model.Task;
 import com.android.systemui.recents.model.TaskStack;
@@ -117,31 +118,58 @@
             final Rect windowRect = Recents.getSystemServices().getWindowRect();
             transitionFuture = getAppTransitionFuture(
                     () -> composeAnimationSpecs(task, stackView, destinationStack, windowRect));
-            animStartedListener = () -> {
-                // If we are launching into another task, cancel the previous task's
-                // window transition
-                EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
-                EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
-                stackView.cancelAllTaskViewAnimations();
+            animStartedListener = new OnAnimationStartedListener() {
+                private boolean mHandled;
 
-                if (screenPinningRequested) {
-                    // Request screen pinning after the animation runs
-                    mStartScreenPinningRunnable.taskId = task.key.id;
-                    mHandler.postDelayed(mStartScreenPinningRunnable, 350);
+                @Override
+                public void onAnimationStarted() {
+                    if (mHandled) {
+                        return;
+                    }
+                    mHandled = true;
+
+                    // If we are launching into another task, cancel the previous task's
+                    // window transition
+                    EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
+                    EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
+                    stackView.cancelAllTaskViewAnimations();
+
+                    if (screenPinningRequested) {
+                        // Request screen pinning after the animation runs
+                        mStartScreenPinningRunnable.taskId = task.key.id;
+                        mHandler.postDelayed(mStartScreenPinningRunnable, 350);
+                    }
+
+                    // Reset the state where we are waiting for the transition to start
+                    EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
                 }
             };
         } else {
             // This is only the case if the task is not on screen (scrolled offscreen for example)
             transitionFuture = null;
-            animStartedListener = () -> {
-                // If we are launching into another task, cancel the previous task's
-                // window transition
-                EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
-                EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
-                stackView.cancelAllTaskViewAnimations();
+            animStartedListener = new OnAnimationStartedListener() {
+                private boolean mHandled;
+
+                @Override
+                public void onAnimationStarted() {
+                    if (mHandled) {
+                        return;
+                    }
+                    mHandled = true;
+
+                    // If we are launching into another task, cancel the previous task's
+                    // window transition
+                    EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task));
+                    EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent());
+                    stackView.cancelAllTaskViewAnimations();
+
+                    // Reset the state where we are waiting for the transition to start
+                    EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false));
+                }
             };
         }
 
+        EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(true));
         final ActivityOptions opts = ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(mContext,
                 mHandler, transitionFuture != null ? transitionFuture.future : null,
                 animStartedListener, true /* scaleUp */);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 15b682e..a633a3e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -219,6 +219,7 @@
     private boolean mResetToInitialStateWhenResized;
     private int mLastWidth;
     private int mLastHeight;
+    private boolean mStackActionButtonVisible;
 
     // We keep track of the task view focused by user interaction and draw a frame around it in the
     // grid layout.
@@ -287,6 +288,7 @@
         mDividerSize = ssp.getDockedDividerSize(context);
         mDisplayOrientation = Utilities.getAppConfiguration(mContext).orientation;
         mDisplayRect = ssp.getDisplayRect();
+        mStackActionButtonVisible = false;
 
         // Create a frame to draw around the focused task view
         if (Recents.getConfiguration().isGridEnabled) {
@@ -975,6 +977,9 @@
                 mLayoutAlgorithm.clearUnfocusedTaskOverrides();
                 willScroll = mAnimationHelper.startScrollToFocusedTaskAnimation(newFocusedTask,
                         requestViewFocus);
+                if (willScroll) {
+                    sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
+                }
             } else {
                 // Focus the task view
                 TaskView newFocusedTaskView = getChildViewForTask(newFocusedTask);
@@ -1159,7 +1164,7 @@
             Task focusedTask = getAccessibilityFocusedTask();
             info.setScrollable(true);
             int focusedTaskIndex = mStack.indexOfStackTask(focusedTask);
-            if (focusedTaskIndex > 0) {
+            if (focusedTaskIndex > 0 || !mStackActionButtonVisible) {
                 info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
             }
             if (0 <= focusedTaskIndex && focusedTaskIndex < mStack.getTaskCount() - 1) {
@@ -1812,6 +1817,18 @@
         }
     }
 
+    public final void onBusEvent(ShowStackActionButtonEvent event) {
+        if (RecentsDebugFlags.Static.EnableStackActionButton) {
+            mStackActionButtonVisible = true;
+        }
+    }
+
+    public final void onBusEvent(HideStackActionButtonEvent event) {
+        if (RecentsDebugFlags.Static.EnableStackActionButton) {
+            mStackActionButtonVisible = false;
+        }
+    }
+
     public final void onBusEvent(LaunchNextTaskRequestEvent event) {
         if (!mFinishedLayoutAfterStackReload) {
             mLaunchNextAfterFirstMeasure = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 41db927..5eefe9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -70,6 +70,7 @@
     private boolean mHasItemsInStableShelf;
     private NotificationIconContainer mCollapsedIcons;
     private int mScrollFastThreshold;
+    private int mIconSize;
     private int mStatusBarState;
     private float mMaxShelfEnd;
     private int mRelativeOffset;
@@ -135,6 +136,7 @@
         mShelfIcons.setPadding(padding, 0, padding, 0);
         mScrollFastThreshold = res.getDimensionPixelOffset(R.dimen.scroll_fast_threshold);
         mShowNotificationShelf = res.getBoolean(R.bool.config_showNotificationShelf);
+        mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size);
 
         if (!mShowNotificationShelf) {
             setVisibility(GONE);
@@ -501,12 +503,12 @@
         }
         notificationIconPosition += iconTopPadding;
         float shelfIconPosition = getTranslationY() + icon.getTop();
-        shelfIconPosition += ((1.0f - icon.getIconScale()) * icon.getHeight()) / 2.0f;
+        shelfIconPosition += (icon.getHeight() - icon.getIconScale() * mIconSize) / 2.0f;
         float iconYTranslation = NotificationUtils.interpolate(
                 notificationIconPosition - shelfIconPosition,
                 0,
                 transitionAmount);
-        float shelfIconSize = icon.getHeight() * icon.getIconScale();
+        float shelfIconSize = mIconSize * icon.getIconScale();
         float alpha = 1.0f;
         boolean noIcon = !row.isShowingIcon();
         if (noIcon) {
@@ -518,7 +520,7 @@
         float newSize = NotificationUtils.interpolate(notificationIconSize, shelfIconSize,
                 transitionAmount);
         if (iconState != null) {
-            iconState.scaleX = newSize / icon.getHeight() / icon.getIconScale();
+            iconState.scaleX = newSize / shelfIconSize;
             iconState.scaleY = iconState.scaleX;
             iconState.hidden = transitionAmount == 0.0f && !iconState.isAnimating(icon);
             boolean isAppearing = row.isDrawingAppearAnimation() && !row.isInShelf();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index cc4c313..2cff79d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -572,14 +572,14 @@
 
     /**
      * Updates {@param array} such that it represents a matrix that changes RGB to {@param color}
-     * and multiplies A with 1+{@param alphaBoost}.
+     * and multiplies the alpha channel with the color's alpha+{@param alphaBoost}.
      */
     private static void updateTintMatrix(float[] array, int color, float alphaBoost) {
         Arrays.fill(array, 0);
         array[4] = Color.red(color);
         array[9] = Color.green(color);
         array[14] = Color.blue(color);
-        array[18] = 1 + alphaBoost;
+        array[18] = Color.alpha(color) / 255f + alphaBoost;
     }
 
     public void setIconColor(int iconColor, boolean animate) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
index 2283c13..3794ac6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java
@@ -56,6 +56,8 @@
     private boolean mWakeAndUnlocking;
     private boolean mFullyPulsing;
 
+    private float mAodFrontScrimOpacity = 0;
+
     public DozeScrimController(ScrimController scrimController, Context context) {
         mContext = context;
         mScrimController = scrimController;
@@ -70,7 +72,8 @@
             mDozingAborted = false;
             abortAnimations();
             mScrimController.setDozeBehindAlpha(1f);
-            mScrimController.setDozeInFrontAlpha(mDozeParameters.getAlwaysOn() ? 0f : 1f);
+            mScrimController.setDozeInFrontAlpha(
+                    mDozeParameters.getAlwaysOn() ? mAodFrontScrimOpacity : 1f);
         } else {
             cancelPulsing();
             if (animate) {
@@ -88,6 +91,19 @@
         }
     }
 
+    /**
+     * Set the opacity of the front scrim when showing AOD1
+     *
+     * Used to emulate lower brightness values than the hardware supports natively.
+     */
+    public void setAodDimmingScrim(float scrimOpacity) {
+        mAodFrontScrimOpacity = scrimOpacity;
+        if (mDozing && !isPulsing() && !mDozingAborted && !mWakeAndUnlocking
+                && mDozeParameters.getAlwaysOn()) {
+            mScrimController.setDozeInFrontAlpha(mAodFrontScrimOpacity);
+        }
+    }
+
     public void setWakeAndUnlocking() {
         // Immediately abort the doze scrims in case of wake-and-unlock
         // for pulsing so the Keyguard fade-out animation scrim can take over.
@@ -126,7 +142,8 @@
         if (mDozing && !mWakeAndUnlocking) {
             mScrimController.setDozeBehindAlpha(1f);
             mScrimController.setDozeInFrontAlpha(
-                    mDozeParameters.getAlwaysOn() && !mDozingAborted ? 0f : 1f);
+                    mDozeParameters.getAlwaysOn() && !mDozingAborted ?
+                            mAodFrontScrimOpacity : 1f);
         }
     }
 
@@ -337,7 +354,7 @@
             // Signal that the pulse is all finished so we can turn the screen off now.
             pulseFinished();
             if (mDozeParameters.getAlwaysOn()) {
-                mScrimController.setDozeInFrontAlpha(0);
+                mScrimController.setDozeInFrontAlpha(mAodFrontScrimOpacity);
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
index 02202cf..cb96dea 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FingerprintUnlockController.java
@@ -29,6 +29,7 @@
 import com.android.keyguard.LatencyTracker;
 import com.android.systemui.Dependency;
 import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
 
 /**
@@ -101,6 +102,7 @@
     private final Context mContext;
     private int mPendingAuthenticatedUserId = -1;
     private boolean mPendingShowBouncer;
+    private boolean mHasScreenTurnedOnSinceAuthenticating;
 
     public FingerprintUnlockController(Context context,
             DozeScrimController dozeScrimController,
@@ -113,6 +115,7 @@
         mUpdateMonitor = KeyguardUpdateMonitor.getInstance(context);
         mUpdateMonitor.registerCallback(this);
         Dependency.get(WakefulnessLifecycle.class).addObserver(mWakefulnessObserver);
+        Dependency.get(ScreenLifecycle.class).addObserver(mScreenObserver);
         mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
         mDozeScrimController = dozeScrimController;
         mKeyguardViewMediator = keyguardViewMediator;
@@ -184,8 +187,13 @@
             Trace.endSection();
             return;
         }
+        startWakeAndUnlock(calculateMode());
+    }
+
+    public void startWakeAndUnlock(int mode) {
         boolean wasDeviceInteractive = mUpdateMonitor.isDeviceInteractive();
-        mMode = calculateMode();
+        mMode = mode;
+        mHasScreenTurnedOnSinceAuthenticating = false;
         if (mMode == MODE_WAKE_AND_UNLOCK_PULSING && pulsingOrAod()) {
             // If we are waking the device up while we are pulsing the clock and the
             // notifications would light up first, creating an unpleasant animation.
@@ -228,6 +236,7 @@
                             true /* allowEnterAnimation */);
                 } else {
                     Trace.beginSection("MODE_WAKE_AND_UNLOCK");
+
                     mDozeScrimController.abortDoze();
                 }
                 mStatusBarWindowManager.setStatusBarFocusable(false);
@@ -354,4 +363,16 @@
             }
         }
     };
+
+    private final ScreenLifecycle.Observer mScreenObserver =
+            new ScreenLifecycle.Observer() {
+                @Override
+                public void onScreenTurnedOn() {
+                    mHasScreenTurnedOnSinceAuthenticating = true;
+                }
+            };
+
+    public boolean hasScreenTurnedOnSinceAuthenticating() {
+        return mHasScreenTurnedOnSinceAuthenticating;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 49bac99..fd95cc4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -234,8 +234,11 @@
     }
 
     protected void ensureView() {
-        mHandler.removeCallbacks(mRemoveViewRunnable);
-        if (mRoot == null) {
+        // Removal of the view might be deferred to reduce unlock latency,
+        // in this case we need to force the removal, otherwise we'll
+        // end up in an unpredictable state.
+        boolean forceRemoval = mHandler.hasCallbacks(mRemoveViewRunnable);
+        if (mRoot == null || forceRemoval) {
             inflateView();
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index c487901..87f5ca7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -158,7 +158,7 @@
     }
 
     @Override
-    public void onWallpaperColorsChanged(WallpaperColors colors, int which) {
+    public void onWallpaperColorsChanged(WallpaperColors colors, int which, int userId) {
 
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java
index 8bc6563..004a604 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NearestTouchFrame.java
@@ -15,7 +15,9 @@
 package com.android.systemui.statusbar.phone;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.support.annotation.VisibleForTesting;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Pair;
@@ -42,8 +44,13 @@
     private View mTouchingChild;
 
     public NearestTouchFrame(Context context, AttributeSet attrs) {
+        this(context, attrs, context.getResources().getConfiguration());
+    }
+
+    @VisibleForTesting
+    NearestTouchFrame(Context context, AttributeSet attrs, Configuration c) {
         super(context, attrs);
-        mIsActive = context.getResources().getConfiguration().smallestScreenWidthDp < 600;
+        mIsActive = c.smallestScreenWidthDp < 600;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index f9dd8bf..1d64480 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -516,6 +516,7 @@
         } else {
             scrim.setAlpha(alpha1);
         }
+        dispatchScrimsVisible();
     }
 
     private void startScrimAnimation(final View scrim, float target) {
@@ -542,12 +543,13 @@
                     mKeyguardFadingOutInProgress = false;
                     mAnimatingDozeUnlock = false;
                 }
-                if (mWakingUpFromAodAnimationRunning) {
+                if (mWakingUpFromAodAnimationRunning && !mDeferFinishedListener) {
                     mWakingUpFromAodAnimationRunning = false;
                     mWakingUpFromAodInProgress = false;
                 }
                 scrim.setTag(TAG_KEY_ANIM, null);
                 scrim.setTag(TAG_KEY_ANIM_TARGET, null);
+                dispatchScrimsVisible();
             }
         });
         anim.start();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index ab021b4..59e7005 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -142,6 +142,7 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.colorextraction.ColorExtractor;
 import com.android.internal.graphics.ColorUtils;
 import com.android.internal.logging.MetricsLogger;
@@ -749,7 +750,7 @@
     private SysuiColorExtractor mColorExtractor;
     private ForegroundServiceController mForegroundServiceController;
     private ScreenLifecycle mScreenLifecycle;
-    private WakefulnessLifecycle mWakefulnessLifecycle;
+    @VisibleForTesting WakefulnessLifecycle mWakefulnessLifecycle;
 
     private void recycleAllVisibilityObjects(ArraySet<NotificationVisibility> array) {
         final int N = array.size();
@@ -3787,6 +3788,15 @@
 
     private void dismissKeyguardThenExecute(OnDismissAction action, Runnable cancelAction,
             boolean afterKeyguardGone) {
+        if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_ASLEEP
+                && mUnlockMethodCache.canSkipBouncer()
+                && !mLeaveOpenOnKeyguardHide
+                && isPulsing()) {
+            // Reuse the fingerprint wake-and-unlock transition if we dismiss keyguard from a pulse.
+            // TODO: Factor this transition out of FingerprintUnlockController.
+            mFingerprintUnlockController.startWakeAndUnlock(
+                    FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
+        }
         if (mStatusBarKeyguardViewManager.isShowing()) {
             mStatusBarKeyguardViewManager.dismissWithAction(action, cancelAction,
                     afterKeyguardGone);
@@ -4223,7 +4233,10 @@
 
     public void showKeyguard() {
         mKeyguardRequested = true;
+        mLeaveOpenOnKeyguardHide = false;
+        mPendingRemoteInputView = null;
         updateIsKeyguard();
+        mAssistManager.onLockscreenShown();
     }
 
     public boolean hideKeyguard() {
@@ -4272,14 +4285,11 @@
         }
         updateKeyguardState(false /* goingToFullShade */, false /* fromShadeLocked */);
         updatePanelExpansionForKeyguard();
-        mLeaveOpenOnKeyguardHide = false;
         if (mDraggedDownRow != null) {
             mDraggedDownRow.setUserLocked(false);
             mDraggedDownRow.notifyHeightChanged(false  /* needsAnimation */);
             mDraggedDownRow = null;
         }
-        mPendingRemoteInputView = null;
-        mAssistManager.onLockscreenShown();
     }
 
     private void updatePanelExpansionForKeyguard() {
@@ -4419,15 +4429,19 @@
         setBarState(StatusBarState.SHADE);
         View viewToClick = null;
         if (mLeaveOpenOnKeyguardHide) {
-            mLeaveOpenOnKeyguardHide = false;
+            if (!mKeyguardRequested) {
+                mLeaveOpenOnKeyguardHide = false;
+            }
             long delay = calculateGoingToFullShadeDelay();
             mNotificationPanel.animateToFullShade(delay);
             if (mDraggedDownRow != null) {
                 mDraggedDownRow.setUserLocked(false);
                 mDraggedDownRow = null;
             }
-            viewToClick = mPendingRemoteInputView;
-            mPendingRemoteInputView = null;
+            if (!mKeyguardRequested) {
+                viewToClick = mPendingRemoteInputView;
+                mPendingRemoteInputView = null;
+            }
 
             // Disable layout transitions in navbar for this transition because the load is just
             // too heavy for the CPU and GPU on any device.
@@ -5566,6 +5580,11 @@
             mStatusBarWindowManager.setDozeScreenBrightness(value);
         }
 
+        @Override
+        public void setAodDimmingScrim(float scrimOpacity) {
+            mDozeScrimController.setAodDimmingScrim(scrimOpacity);
+        }
+
         public void dispatchDoubleTap(float viewX, float viewY) {
             dispatchTap(mAmbientIndicationContainer, viewX, viewY);
             dispatchTap(mAmbientIndicationContainer, viewX, viewY);
@@ -5778,15 +5797,18 @@
                         boolean handled = superOnClickHandler(view, pendingIntent, fillInIntent);
 
                         // close the shade if it was open
-                        if (handled) {
+                        if (handled && !mNotificationPanel.isFullyCollapsed()) {
                             animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
                                     true /* force */);
                             visibilityChanged(false);
                             mAssistManager.hideAssist();
+
+                            // Wait for activity start.
+                            return true;
+                        } else {
+                            return false;
                         }
 
-                        // Wait for activity start.
-                        return handled;
                     }
                 }, afterKeyguardGone);
                 return true;
@@ -6836,12 +6858,16 @@
                     }
                 }.start();
 
-                // close the shade if it was open
-                animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
-                        true /* force */, true /* delayed */);
-                visibilityChanged(false);
+                if (!mNotificationPanel.isFullyCollapsed()) {
+                    // close the shade if it was open
+                    animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
+                            true /* force */, true /* delayed */);
+                    visibilityChanged(false);
 
-                return true;
+                    return true;
+                } else {
+                    return false;
+                }
             }
         }, afterKeyguardGone);
     }
@@ -6993,12 +7019,16 @@
                         new Thread(runnable).start();
                     }
 
-                    // close the shade if it was open
-                    animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
-                            true /* force */, true /* delayed */);
-                    visibilityChanged(false);
+                    if (!mNotificationPanel.isFullyCollapsed()) {
+                        // close the shade if it was open
+                        animateCollapsePanels(CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
+                                true /* force */, true /* delayed */);
+                        visibilityChanged(false);
 
-                    return true;
+                        return true;
+                    } else {
+                        return false;
+                    }
                 }
             }, afterKeyguardGone);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 252df17..06cdfae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -71,6 +71,7 @@
 
     protected final Context mContext;
     private final StatusBarWindowManager mStatusBarWindowManager;
+    private final boolean mDisplayBlanksAfterDoze;
 
     protected LockPatternUtils mLockPatternUtils;
     protected ViewMediatorCallback mViewMediatorCallback;
@@ -101,6 +102,9 @@
     private final ArrayList<Runnable> mAfterKeyguardGoneRunnables = new ArrayList<>();
     private boolean mDeferScrimFadeOut;
 
+    // Dismiss action to be launched when we stop dozing or the keyguard is gone.
+    private DismissWithActionRequest mPendingWakeupAction;
+
     private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
             new KeyguardUpdateMonitorCallback() {
         @Override
@@ -121,6 +125,8 @@
         mLockPatternUtils = lockPatternUtils;
         mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
         KeyguardUpdateMonitor.getInstance(context).registerCallback(mUpdateMonitorCallback);
+        mDisplayBlanksAfterDoze = context.getResources().getBoolean(
+                com.android.internal.R.bool.config_displayBlanksAfterDoze);
     }
 
     public void registerStatusBar(StatusBar statusBar,
@@ -153,17 +159,22 @@
      */
     protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
         if (mBouncer.needsFullscreenBouncer() && !mDozing) {
-
             // The keyguard might be showing (already). So we need to hide it.
             mStatusBar.hideKeyguard();
             mBouncer.show(true /* resetSecuritySelection */);
         } else {
             mStatusBar.showKeyguard();
             if (hideBouncerWhenShowing) {
-                mBouncer.hide(false /* destroyView */);
+                hideBouncer(false /* destroyView */);
                 mBouncer.prepare();
             }
         }
+        updateStates();
+    }
+
+    private void hideBouncer(boolean destroyView) {
+        mBouncer.hide(destroyView);
+        cancelPendingWakeupAction();
     }
 
     private void showBouncer() {
@@ -176,6 +187,15 @@
     public void dismissWithAction(OnDismissAction r, Runnable cancelAction,
             boolean afterKeyguardGone) {
         if (mShowing) {
+            cancelPendingWakeupAction();
+            // If we're dozing, this needs to be delayed until after we wake up - unless we're
+            // wake-and-unlocking, because there dozing will last until the end of the transition.
+            if (mDozing && !isWakeAndUnlocking()) {
+                mPendingWakeupAction = new DismissWithActionRequest(
+                        r, cancelAction, afterKeyguardGone);
+                return;
+            }
+
             if (!afterKeyguardGone) {
                 mBouncer.showWithDismissAction(r, cancelAction);
             } else {
@@ -186,6 +206,11 @@
         updateStates();
     }
 
+    private boolean isWakeAndUnlocking() {
+        int mode = mFingerprintUnlockController.getMode();
+        return mode == MODE_WAKE_AND_UNLOCK || mode == MODE_WAKE_AND_UNLOCK_PULSING;
+    }
+
     /**
      * Adds a {@param runnable} to be executed after Keyguard is gone.
      */
@@ -198,10 +223,12 @@
      */
     public void reset(boolean hideBouncerWhenShowing) {
         if (mShowing) {
-            if (mOccluded) {
+            if (mOccluded && !mDozing) {
                 mStatusBar.hideKeyguard();
                 mStatusBar.stopWaitingForKeyguardExit();
-                mBouncer.hide(false /* destroyView */);
+                if (hideBouncerWhenShowing || mBouncer.needsFullscreenBouncer()) {
+                    hideBouncer(false /* destroyView */);
+                }
             } else {
                 showBouncerOrKeyguard(hideBouncerWhenShowing);
             }
@@ -247,8 +274,14 @@
     public void setDozing(boolean dozing) {
         if (mDozing != dozing) {
             mDozing = dozing;
-            reset(dozing /* hideBouncerWhenShowing */);
+            if (dozing || mBouncer.needsFullscreenBouncer() || mOccluded) {
+                reset(dozing /* hideBouncerWhenShowing */);
+            }
             updateStates();
+
+            if (!dozing) {
+                launchPendingWakeupAction();
+            }
         }
     }
 
@@ -289,9 +322,12 @@
         }
         mStatusBarWindowManager.setKeyguardOccluded(occluded);
 
-        // If Keyguard is reshown, don't hide the bouncer as it might just have been requested by
-        // a FLAG_DISMISS_KEYGUARD_ACTIVITY.
-        reset(false /* hideBouncerWhenShowing*/);
+        // setDozing(false) will call reset once we stop dozing.
+        if (!mDozing) {
+            // If Keyguard is reshown, don't hide the bouncer as it might just have been requested
+            // by a FLAG_DISMISS_KEYGUARD_ACTIVITY.
+            reset(false /* hideBouncerWhenShowing*/);
+        }
         if (animate && !occluded && mShowing) {
             mStatusBar.animateKeyguardUnoccluding();
         }
@@ -321,6 +357,7 @@
      */
     public void hide(long startTime, long fadeoutDuration) {
         mShowing = false;
+        launchPendingWakeupAction();
 
         if (KeyguardUpdateMonitor.getInstance(mContext).needsSlowUnlockTransition()) {
             fadeoutDuration = KEYGUARD_DISMISS_DURATION_LOCKED;
@@ -334,7 +371,7 @@
                 public void run() {
                     mStatusBarWindowManager.setKeyguardShowing(false);
                     mStatusBarWindowManager.setKeyguardFadingAway(true);
-                    mBouncer.hide(true /* destroyView */);
+                    hideBouncer(true /* destroyView */);
                     updateStates();
                     mScrimController.animateKeyguardFadingOut(
                             StatusBar.FADE_KEYGUARD_START_DELAY,
@@ -360,7 +397,7 @@
             }
             mStatusBar.setKeyguardFadingAway(startTime, delay, fadeoutDuration);
             mFingerprintUnlockController.startKeyguardFadingAway();
-            mBouncer.hide(true /* destroyView */);
+            hideBouncer(true /* destroyView */);
             if (wakeUnlockPulsing) {
                 mStatusBarWindowManager.setKeyguardFadingAway(true);
                 mStatusBar.fadeKeyguardWhilePulsing();
@@ -373,7 +410,11 @@
                 if (!staying) {
                     mStatusBarWindowManager.setKeyguardFadingAway(true);
                     if (mFingerprintUnlockController.getMode() == MODE_WAKE_AND_UNLOCK) {
-                        if (!mScreenTurnedOn) {
+                        boolean turnedOnSinceAuth =
+                                mFingerprintUnlockController.hasScreenTurnedOnSinceAuthenticating();
+                        if (!mScreenTurnedOn || mDisplayBlanksAfterDoze && !turnedOnSinceAuth) {
+                            // Not ready to animate yet; either because the screen is not on yet,
+                            // or it is on but will turn off before waking out of doze.
                             mDeferScrimFadeOut = true;
                         } else {
 
@@ -399,11 +440,12 @@
     }
 
     public void onDensityOrFontScaleChanged() {
-        mBouncer.hide(true /* destroyView */);
+        hideBouncer(true /* destroyView */);
     }
 
     public void onOverlayChanged() {
-        mBouncer.hide(true /* destroyView */);
+        hideBouncer(true /* destroyView */);
+        mBouncer.prepare();
     }
 
     private void animateScrimControllerKeyguardFadingOut(long delay, long duration,
@@ -642,4 +684,38 @@
     public ViewRootImpl getViewRootImpl() {
         return mStatusBar.getStatusBarView().getViewRootImpl();
     }
+
+    public void launchPendingWakeupAction() {
+        DismissWithActionRequest request = mPendingWakeupAction;
+        mPendingWakeupAction = null;
+        if (request != null) {
+            if (mShowing) {
+                dismissWithAction(request.dismissAction, request.cancelAction,
+                        request.afterKeyguardGone);
+            } else if (request.dismissAction != null) {
+                request.dismissAction.onDismiss();
+            }
+        }
+    }
+
+    public void cancelPendingWakeupAction() {
+        DismissWithActionRequest request = mPendingWakeupAction;
+        mPendingWakeupAction = null;
+        if (request != null && request.cancelAction != null) {
+            request.cancelAction.run();
+        }
+    }
+
+    private static class DismissWithActionRequest {
+        final OnDismissAction dismissAction;
+        final Runnable cancelAction;
+        final boolean afterKeyguardGone;
+
+        DismissWithActionRequest(OnDismissAction dismissAction, Runnable cancelAction,
+                boolean afterKeyguardGone) {
+            this.dismissAction = dismissAction;
+            this.cancelAction = cancelAction;
+            this.afterKeyguardGone = afterKeyguardGone;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 472e30c..188f216 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -3714,6 +3714,9 @@
      * See {@link AmbientState#setDark}.
      */
     public void setDark(boolean dark, boolean animate, @Nullable PointF touchWakeUpScreenLocation) {
+        if (mAmbientState.isDark() == dark) {
+            return;
+        }
         mAmbientState.setDark(dark);
         if (animate && mAnimationsEnabled) {
             mDarkNeedsAnimation = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index f4197a3..c060b08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -249,14 +249,28 @@
         state.paddingMap.clear();
         int notGoneIndex = 0;
         ExpandableView lastView = null;
+        int firstHiddenIndex = ambientState.isDark()
+                ? (ambientState.hasPulsingNotifications() ? 1 : 0)
+                : childCount;
+
+        // The goal here is to fill the padding map, by iterating over how much padding each child
+        // needs. The map is thereby reused, by first filling it with the padding amount and when
+        // iterating over it again, it's filled with the actual resolved value.
+
         for (int i = 0; i < childCount; i++) {
             ExpandableView v = (ExpandableView) hostView.getChildAt(i);
             if (v.getVisibility() != View.GONE) {
                 if (v == ambientState.getShelf()) {
                     continue;
                 }
+                if (i >= firstHiddenIndex) {
+                    // we need normal padding now, to be in sync with what the stack calculates
+                    lastView = null;
+                    ExpandableViewState viewState = resultState.getViewStateForView(v);
+                    viewState.hidden = true;
+                }
                 notGoneIndex = updateNotGoneIndex(resultState, state, notGoneIndex, v);
-                float increasedPadding = v.getIncreasedPaddingAmount();;
+                float increasedPadding = v.getIncreasedPaddingAmount();
                 if (increasedPadding != 0.0f) {
                     state.paddingMap.put(v, increasedPadding);
                     if (lastView != null) {
@@ -279,6 +293,8 @@
                         state.paddingMap.put(lastView, newValue);
                     }
                 } else if (lastView != null) {
+
+                    // Let's now resolve the value to an actual padding
                     float newValue = getPaddingForValue(state.paddingMap.get(lastView));
                     state.paddingMap.put(lastView, newValue);
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java b/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java
new file mode 100644
index 0000000..5790ba3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/AsyncSensorManager.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+import android.hardware.HardwareBuffer;
+import android.hardware.Sensor;
+import android.hardware.SensorAdditionalInfo;
+import android.hardware.SensorDirectChannel;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.TriggerEventListener;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.MemoryFile;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * Wrapper around sensor manager that hides potential sources of latency.
+ *
+ * Offloads fetching (non-dynamic) sensors and (un)registering listeners onto a background thread
+ * without blocking. Note that this means registering listeners now always appears successful even
+ * if it is not.
+ */
+public class AsyncSensorManager extends SensorManager {
+
+    private static final String TAG = "AsyncSensorManager";
+
+    private final SensorManager mInner;
+    private final List<Sensor> mSensorCache;
+    private final HandlerThread mHandlerThread = new HandlerThread("async_sensor");
+    @VisibleForTesting final Handler mHandler;
+
+    public AsyncSensorManager(SensorManager inner) {
+        mInner = inner;
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mSensorCache = mInner.getSensorList(Sensor.TYPE_ALL);
+    }
+
+    @Override
+    protected List<Sensor> getFullSensorList() {
+        return mSensorCache;
+    }
+
+    @Override
+    protected List<Sensor> getFullDynamicSensorList() {
+        return mInner.getDynamicSensorList(Sensor.TYPE_ALL);
+    }
+
+    @Override
+    protected boolean registerListenerImpl(SensorEventListener listener, Sensor sensor, int delayUs,
+            Handler handler, int maxReportLatencyUs, int reservedFlags) {
+        mHandler.post(() -> {
+            if (!mInner.registerListener(listener, sensor, delayUs, maxReportLatencyUs, handler)) {
+                Log.e(TAG, "Registering " + listener + " for " + sensor + " failed.");
+            }
+        });
+        return true;
+    }
+
+    @Override
+    protected boolean flushImpl(SensorEventListener listener) {
+        return mInner.flush(listener);
+    }
+
+    @Override
+    protected SensorDirectChannel createDirectChannelImpl(MemoryFile memoryFile,
+            HardwareBuffer hardwareBuffer) {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    protected void destroyDirectChannelImpl(SensorDirectChannel channel) {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    protected int configureDirectChannelImpl(SensorDirectChannel channel, Sensor s, int rate) {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    protected void registerDynamicSensorCallbackImpl(DynamicSensorCallback callback,
+            Handler handler) {
+        mHandler.post(() -> mInner.registerDynamicSensorCallback(callback, handler));
+    }
+
+    @Override
+    protected void unregisterDynamicSensorCallbackImpl(DynamicSensorCallback callback) {
+        mHandler.post(() -> mInner.unregisterDynamicSensorCallback(callback));
+    }
+
+    @Override
+    protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) {
+        mHandler.post(() -> {
+            if (!mInner.requestTriggerSensor(listener, sensor)) {
+                Log.e(TAG, "Requesting " + listener + " for " + sensor + " failed.");
+            }
+        });
+        return true;
+    }
+
+    @Override
+    protected boolean cancelTriggerSensorImpl(TriggerEventListener listener, Sensor sensor,
+            boolean disable) {
+        Preconditions.checkArgument(disable);
+
+        mHandler.post(() -> {
+            if (!mInner.cancelTriggerSensor(listener, sensor)) {
+                Log.e(TAG, "Canceling " + listener + " for " + sensor + " failed.");
+            }
+        });
+        return true;
+    }
+
+    @Override
+    protected boolean initDataInjectionImpl(boolean enable) {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    protected boolean injectSensorDataImpl(Sensor sensor, float[] values, int accuracy,
+            long timestamp) {
+        throw new UnsupportedOperationException("not implemented");
+    }
+
+    @Override
+    protected boolean setOperationParameterImpl(SensorAdditionalInfo parameter) {
+        mHandler.post(() -> mInner.setOperationParameter(parameter));
+        return true;
+    }
+
+    @Override
+    protected void unregisterListenerImpl(SensorEventListener listener, Sensor sensor) {
+        mHandler.post(() -> {
+            if (sensor == null) {
+                mInner.unregisterListener(listener);
+            } else {
+                mInner.unregisterListener(listener, sensor);
+            }
+        });
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index ad47411..b08b26d 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -100,7 +100,7 @@
     private final MediaSessions mMediaSessions;
     protected C mCallbacks = new C();
     private final State mState = new State();
-    private final MediaSessionsCallbacks mMediaSessionsCallbacksW = new MediaSessionsCallbacks();
+    protected final MediaSessionsCallbacks mMediaSessionsCallbacksW = new MediaSessionsCallbacks();
     private final Vibrator mVibrator;
     private final boolean mHasVibrator;
     private boolean mShowA11yStream;
@@ -906,7 +906,7 @@
         }
     }
 
-    private final class MediaSessionsCallbacks implements MediaSessions.Callbacks {
+    protected final class MediaSessionsCallbacks implements MediaSessions.Callbacks {
         private final HashMap<Token, Integer> mRemoteStreams = new HashMap<>();
 
         private int mNextStream = DYNAMIC_STREAM_START_INDEX;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index fe3221a..c275806 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -51,14 +51,16 @@
     DozeScreenBrightness mScreen;
     FakeSensorManager.FakeGenericSensor mSensor;
     FakeSensorManager mSensorManager;
+    DozeHostFake mHostFake;
 
     @Before
     public void setUp() throws Exception {
         mServiceFake = new DozeServiceFake();
+        mHostFake = new DozeHostFake();
         mSensorManager = new FakeSensorManager(mContext);
         mSensor = mSensorManager.getFakeLightSensor();
         mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
-                mSensor.getSensor(), null /* handler */);
+                mSensor.getSensor(), mHostFake, null /* handler */);
     }
 
     @Test
@@ -133,7 +135,7 @@
     @Test
     public void testNullSensor() throws Exception {
         mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
-                null /* sensor */, null /* handler */);
+                null /* sensor */, mHostFake, null /* handler */);
 
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java
index 577dc52..ed1491d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NearestTouchFrameTest.java
@@ -18,10 +18,12 @@
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.res.Configuration;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
@@ -43,7 +45,29 @@
 
     @Before
     public void setup() {
-        mNearestTouchFrame = new NearestTouchFrame(mContext, null);
+        Configuration c = new Configuration(mContext.getResources().getConfiguration());
+        c.smallestScreenWidthDp = 500;
+        mNearestTouchFrame = new NearestTouchFrame(mContext, null, c);
+    }
+
+    @Test
+    public void testNoActionOnLargeDevices() {
+        Configuration c = new Configuration(mContext.getResources().getConfiguration());
+        c.smallestScreenWidthDp = 700;
+        mNearestTouchFrame = new NearestTouchFrame(mContext, null, c);
+
+        View left = mockViewAt(0, 0, 10, 10);
+        View right = mockViewAt(20, 0, 10, 10);
+
+        mNearestTouchFrame.addView(left);
+        mNearestTouchFrame.addView(right);
+        mNearestTouchFrame.onMeasure(0, 0);
+
+        MotionEvent ev = MotionEvent.obtain(0, 0, 0,
+                12 /* x */, 5 /* y */, 0);
+        mNearestTouchFrame.onTouchEvent(ev);
+        verify(left, never()).onTouchEvent(eq(ev));
+        ev.recycle();
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index c33897e..a706368 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -65,6 +65,7 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.keyguard.KeyguardHostView.OnDismissAction;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.keyguard.WakefulnessLifecycle;
 import com.android.systemui.recents.misc.SystemServicesProxy;
 import com.android.systemui.statusbar.ActivatableNotificationView;
 import com.android.systemui.statusbar.CommandQueue;
@@ -500,6 +501,14 @@
             mSystemServicesProxy = ssp;
             mNotificationPanel = panelView;
             mBarService = barService;
+            mWakefulnessLifecycle = createAwakeWakefulnessLifecycle();
+        }
+
+        private WakefulnessLifecycle createAwakeWakefulnessLifecycle() {
+            WakefulnessLifecycle wakefulnessLifecycle = new WakefulnessLifecycle();
+            wakefulnessLifecycle.dispatchStartedWakingUp();
+            wakefulnessLifecycle.dispatchFinishedWakingUp();
+            return wakefulnessLifecycle;
         }
 
         public void setBarStateForTest(int state) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/AsyncSensorManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/AsyncSensorManagerTest.java
new file mode 100644
index 0000000..469bdc0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/AsyncSensorManagerTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.utils.hardware.FakeSensorManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class AsyncSensorManagerTest extends SysuiTestCase {
+
+    private TestableAsyncSensorManager mAsyncSensorManager;
+    private FakeSensorManager mFakeSensorManager;
+    private SensorEventListener mListener;
+    private FakeSensorManager.MockProximitySensor mSensor;
+
+    @Before
+    public void setUp() throws Exception {
+        mFakeSensorManager = new FakeSensorManager(mContext);
+        mAsyncSensorManager = new TestableAsyncSensorManager(mFakeSensorManager);
+        mSensor = mFakeSensorManager.getMockProximitySensor();
+        mListener = mock(SensorEventListener.class);
+    }
+
+    @Test
+    public void registerListenerImpl() throws Exception {
+        mAsyncSensorManager.registerListener(mListener, mSensor.getSensor(), 100);
+
+        mAsyncSensorManager.waitUntilRequestsCompleted();
+
+        // Verify listener was registered.
+        mSensor.sendProximityResult(true);
+        verify(mListener).onSensorChanged(any());
+    }
+
+    @Test
+    public void unregisterListenerImpl_withNullSensor() throws Exception {
+        mAsyncSensorManager.registerListener(mListener, mSensor.getSensor(), 100);
+        mAsyncSensorManager.unregisterListener(mListener);
+
+        mAsyncSensorManager.waitUntilRequestsCompleted();
+
+        // Verify listener was unregistered.
+        mSensor.sendProximityResult(true);
+        verifyNoMoreInteractions(mListener);
+    }
+
+    @Test
+    public void unregisterListenerImpl_withSensor() throws Exception {
+        mAsyncSensorManager.registerListener(mListener, mSensor.getSensor(), 100);
+        mAsyncSensorManager.unregisterListener(mListener, mSensor.getSensor());
+
+        mAsyncSensorManager.waitUntilRequestsCompleted();
+
+        // Verify listener was unregistered.
+        mSensor.sendProximityResult(true);
+        verifyNoMoreInteractions(mListener);
+    }
+
+    private class TestableAsyncSensorManager extends AsyncSensorManager {
+        public TestableAsyncSensorManager(SensorManager sensorManager) {
+            super(sensorManager);
+        }
+
+        public void waitUntilRequestsCompleted() {
+            assertTrue(mHandler.runWithScissors(() -> {}, 0));
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
index 12e9f7c..06568f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogControllerImplTest.java
@@ -24,6 +24,7 @@
 
 import android.content.Context;
 import android.media.AudioManager;
+import android.media.session.MediaSession;
 import android.support.test.filters.SmallTest;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.keyguard.WakefulnessLifecycle;
@@ -73,6 +74,18 @@
         verify(mCallback, times(1)).onShowRequested(Events.SHOW_REASON_VOLUME_CHANGED);
     }
 
+    @Test
+    public void testOnRemoteVolumeChanged_newStream_noNullPointer() {
+        MediaSession.Token token = new MediaSession.Token(null);
+        mVolumeController.mMediaSessionsCallbacksW.onRemoteVolumeChanged(token, 0);
+    }
+
+    @Test
+    public void testOnRemoteRemove_newStream_noNullPointer() {
+        MediaSession.Token token = new MediaSession.Token(null);
+        mVolumeController.mMediaSessionsCallbacksW.onRemoteRemoved(token);
+    }
+
     static class TestableVolumeDialogControllerImpl extends VolumeDialogControllerImpl {
         public TestableVolumeDialogControllerImpl(Context context, C callback, StatusBar s) {
             super(context);
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index bb6ed74..0befd54 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4262,6 +4262,89 @@
     // OS: O MR
     DATA_PLAN_USAGE_SUMMARY = 1088;
 
+    // OPEN: Settings > Network & Internet > Mobile network
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    SETTINGS_MOBILE_NETWORK_CATEGORY = 1089;
+
+    // ACTION: Settings > Network & Internet > Mobile network > Roaming
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    ACTION_MOBILE_NETWORK_DATA_ROAMING_TOGGLE = 1090;
+
+    // ACTION: Settings > Network & Internet > Mobile network > Advanced
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    ACTION_MOBILE_NETWORK_EXPAND_ADVANCED_FIELDS = 1091;
+
+    // ACTION: Settings > Network & Internet > Mobile network > Enhanced 4G LTE Mode
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    ACTION_MOBILE_ENHANCED_4G_LTE_MODE_TOGGLE = 1092;
+
+    // ACTION: Settings > Network & Internet > Mobile network > Preferred network type
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    ACTION_MOBILE_NETWORK_SELECT_PREFERRED_NETWORK = 1093;
+
+    // ACTION: Settings > Network & Internet > Mobile network > Preferred network type (enabled networks)
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    ACTION_MOBILE_NETWORK_SELECT_ENABLED_NETWORK = 1094;
+
+    // OPEN: Settings > Network & Internet > Mobile network > Carrier
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    ACTION_MOBILE_NETWORK_EUICC_SETTING = 1095;
+
+    // OPEN: Settings > Network & Internet > Mobile network > Wi-Fi calling
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    ACTION_MOBILE_NETWORK_WIFI_CALLING = 1096;
+
+    // ACTION: Settings > Network & Internet > Mobile network > Carrier video calling
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    ACTION_MOBILE_NETWORK_VIDEO_CALLING_TOGGLE = 1097;
+
+    // ACTION: Settings > Network & Internet > Mobile network > Automatically select network
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    ACTION_MOBILE_NETWORK_AUTO_SELECT_NETWORK_TOGGLE = 1098;
+
+    // ACTION: Settings > Network & Internet > Mobile network > Network
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK = 1099;
+
+    // FIELD - Manually selected mobile network
+    FIELD_MOBILE_NETWORK = 1100;
+
+    // OPEN: Settings > Network & Internet > Mobile network > Access Point Names
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    ACTION_MOBILE_NETWORK_APN_SETTINGS = 1101;
+
+    // OPEN: Settings > Network & Internet > Mobile network > Carrier settings
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    ACTION_MOBILE_NETWORK_CARRIER_SETTINGS = 1102;
+
+    // OPEN: Settings > Network & Internet > Mobile network > System select
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    ACTION_MOBILE_NETWORK_CDMA_SYSTEM_SELECT = 1103;
+
+    // OPEN: Settings > Network & Internet > Mobile network > CDMA subscription
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    ACTION_MOBILE_NETWORK_CDMA_SUBSCRIPTION_SELECT = 1104;
+
+    // ACTION: Settings > Network & Internet > Mobile network > Set up data service
+    // CATEGORY: SETTINGS
+    // OS: O MR
+    ACTION_MOBILE_NETWORK_SET_UP_DATA_SERVICE = 1105;
+
     // Add new aosp constants above this line.
     // END OF AOSP CONSTANTS
   }
diff --git a/services/Android.mk b/services/Android.mk
index 0986e0a..ed2ba1f 100644
--- a/services/Android.mk
+++ b/services/Android.mk
@@ -40,8 +40,8 @@
 
 # The convention is to name each service module 'services.$(module_name)'
 LOCAL_STATIC_JAVA_LIBRARIES := $(addprefix services.,$(services)) \
-    android.hidl.base-V1.0-java-static \
-    android.hardware.biometrics.fingerprint-V2.1-java-static
+    android.hidl.base-V1.0-java \
+    android.hardware.biometrics.fingerprint-V2.1-java
 
 LOCAL_JAVA_LIBRARIES := \
     android.hidl.manager-V1.0-java
diff --git a/services/backup/java/com/android/server/backup/AppsBackedUpOnThisDeviceJournal.java b/services/backup/java/com/android/server/backup/AppsBackedUpOnThisDeviceJournal.java
new file mode 100644
index 0000000..c942bb2
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/AppsBackedUpOnThisDeviceJournal.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2017 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.backup;
+
+import static com.android.server.backup.RefactoredBackupManagerService.DEBUG;
+
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.io.EOFException;
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.HashSet;
+
+/**
+ * Records which apps have been backed up on this device, persisting it to disk so that it can be
+ * read at subsequent boots. This class is threadsafe.
+ *
+ * <p>This is used to decide, when restoring a package at install time, whether it has been
+ * previously backed up on the current device. If it has been previously backed up it should
+ * restore from the same restore set that the current device has been backing up to. If it has not
+ * been previously backed up, it should restore from the ancestral restore set (i.e., the restore
+ * set that the user's previous device was backing up to).
+ *
+ * <p>NB: this is always backed by the same files within the state directory supplied at
+ * construction.
+ */
+final class AppsBackedUpOnThisDeviceJournal {
+    private static final String TAG = "AppsBackedUpOnThisDeviceJournal";
+    private static final String JOURNAL_FILE_NAME = "processed";
+
+    @GuardedBy("this")
+    private final HashSet<String> mProcessedPackages = new HashSet<>();
+    private final File mStateDirectory;
+
+    /**
+     * Constructs a new journal, loading state from disk if it has been previously persisted.
+     *
+     * @param stateDirectory The directory in which backup state (including journals) is stored.
+     */
+    AppsBackedUpOnThisDeviceJournal(File stateDirectory) {
+        mStateDirectory = stateDirectory;
+        loadFromDisk();
+    }
+
+    /**
+     * Returns {@code true} if {@code packageName} has previously been backed up.
+     */
+    synchronized boolean hasBeenProcessed(String packageName) {
+        return mProcessedPackages.contains(packageName);
+    }
+
+    synchronized void addPackage(String packageName) {
+        if (!mProcessedPackages.add(packageName)) {
+            // This package has already been processed - no need to add it to the journal.
+            return;
+        }
+
+        File journalFile = new File(mStateDirectory, JOURNAL_FILE_NAME);
+
+        try (RandomAccessFile out = new RandomAccessFile(journalFile, "rws")) {
+            out.seek(out.length());
+            out.writeUTF(packageName);
+        } catch (IOException e) {
+            Slog.e(TAG, "Can't log backup of " + packageName + " to " + journalFile);
+        }
+    }
+
+    /**
+     * A copy of the current state of the journal.
+     *
+     * <p>Used only for dumping out information for logging. {@link #hasBeenProcessed(String)}
+     * should be used for efficiently checking whether a package has been backed up before by this
+     * device.
+     *
+     * @return The current set of packages that have been backed up previously.
+     */
+    synchronized HashSet<String> getPackagesCopy() {
+        return new HashSet<>(mProcessedPackages);
+    }
+
+    synchronized void reset() {
+        mProcessedPackages.clear();
+        File journalFile = new File(mStateDirectory, JOURNAL_FILE_NAME);
+        journalFile.delete();
+    }
+
+    private void loadFromDisk() {
+        File journalFile = new File(mStateDirectory, JOURNAL_FILE_NAME);
+
+        if (!journalFile.exists()) {
+            return;
+        }
+
+        try (RandomAccessFile oldJournal = new RandomAccessFile(journalFile, "r")) {
+            while (true) {
+                String packageName = oldJournal.readUTF();
+                if (DEBUG) {
+                    Slog.v(TAG, "   + " + packageName);
+                }
+                mProcessedPackages.add(packageName);
+            }
+        } catch (EOFException e) {
+            // Successfully loaded journal file
+        } catch (IOException e) {
+            Slog.e(TAG, "Error reading processed packages journal", e);
+        }
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index d118917..39f7232 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -27,7 +27,6 @@
 import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_INIT;
 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_BACKUP;
 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_RESTORE;
-import static com.android.server.backup.internal.BackupHandler.MSG_RUN_BACKUP;
 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_CLEAR;
 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_RESTORE;
 import static com.android.server.backup.internal.BackupHandler.MSG_SCHEDULE_BACKUP_PACKAGE;
@@ -127,14 +126,12 @@
 import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
-import java.io.EOFException;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.PrintWriter;
 import java.io.RandomAccessFile;
 import java.security.SecureRandom;
@@ -630,11 +627,9 @@
 
     private final SecureRandom mRng = new SecureRandom();
 
-    // Keep a log of all the apps we've ever backed up, and what the
-    // dataset tokens are for both the current backup dataset and
-    // the ancestral dataset.
-    private File mEverStored;
-    private HashSet<String> mEverStoredApps = new HashSet<>();
+    // Keep a log of all the apps we've ever backed up, and what the dataset tokens are for both
+    // the current backup dataset and the ancestral dataset.
+    private AppsBackedUpOnThisDeviceJournal mAppsBackedUpOnThisDeviceJournal;
 
     private static final int CURRENT_ANCESTRAL_RECORD_VERSION = 1;
     // increment when the schema changes
@@ -821,49 +816,7 @@
             Slog.w(TAG, "Unable to read token file", e);
         }
 
-        // Keep a log of what apps we've ever backed up.  Because we might have
-        // rebooted in the middle of an operation that was removing something from
-        // this log, we sanity-check its contents here and reconstruct it.
-        mEverStored = new File(mBaseStateDir, "processed");
-        File tempProcessedFile = new File(mBaseStateDir, "processed.new");
-
-        // If we were in the middle of removing something from the ever-backed-up
-        // file, there might be a transient "processed.new" file still present.
-        // Ignore it -- we'll validate "processed" against the current package set.
-        if (tempProcessedFile.exists()) {
-            tempProcessedFile.delete();
-        }
-
-        // If there are previous contents, parse them out then start a new
-        // file to continue the recordkeeping.
-        if (mEverStored.exists()) {
-            try (RandomAccessFile temp = new RandomAccessFile(tempProcessedFile, "rws");
-                 RandomAccessFile in = new RandomAccessFile(mEverStored, "r")) {
-                // Loop until we hit EOF
-                while (true) {
-                    String pkg = in.readUTF();
-                    try {
-                        // is this package still present?
-                        mPackageManager.getPackageInfo(pkg, 0);
-                        // if we get here then yes it is; remember it
-                        mEverStoredApps.add(pkg);
-                        temp.writeUTF(pkg);
-                        if (MORE_DEBUG) Slog.v(TAG, "   + " + pkg);
-                    } catch (NameNotFoundException e) {
-                        // nope, this package was uninstalled; don't include it
-                        if (MORE_DEBUG) Slog.v(TAG, "   - " + pkg);
-                    }
-                }
-            } catch (EOFException e) {
-                // Once we've rewritten the backup history log, atomically replace the
-                // old one with the new one then reopen the file for continuing use.
-                if (!tempProcessedFile.renameTo(mEverStored)) {
-                    Slog.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored);
-                }
-            } catch (IOException e) {
-                Slog.e(TAG, "Error in processed file", e);
-            }
-        }
+        mAppsBackedUpOnThisDeviceJournal = new AppsBackedUpOnThisDeviceJournal(mBaseStateDir);
 
         synchronized (mQueueLock) {
             // Resume the full-data backup queue
@@ -1115,9 +1068,7 @@
     // so we must re-upload all saved settings.
     public void resetBackupState(File stateFileDir) {
         synchronized (mQueueLock) {
-            // Wipe the "what we've ever backed up" tracking
-            mEverStoredApps.clear();
-            mEverStored.delete();
+            mAppsBackedUpOnThisDeviceJournal.reset();
 
             mCurrentToken = 0;
             writeRestoreTokens();
@@ -1412,49 +1363,7 @@
     public void logBackupComplete(String packageName) {
         if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
 
-        synchronized (mEverStoredApps) {
-            if (!mEverStoredApps.add(packageName)) return;
-
-            try (RandomAccessFile out = new RandomAccessFile(mEverStored, "rws")) {
-                out.seek(out.length());
-                out.writeUTF(packageName);
-            } catch (IOException e) {
-                Slog.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored);
-            }
-        }
-    }
-
-    // Remove our awareness of having ever backed up the given package
-    void removeEverBackedUp(String packageName) {
-        if (DEBUG) Slog.v(TAG, "Removing backed-up knowledge of " + packageName);
-        if (MORE_DEBUG) Slog.v(TAG, "New set:");
-
-        synchronized (mEverStoredApps) {
-            // Rewrite the file and rename to overwrite.  If we reboot in the middle,
-            // we'll recognize on initialization time that the package no longer
-            // exists and fix it up then.
-            File tempKnownFile = new File(mBaseStateDir, "processed.new");
-            try (RandomAccessFile known = new RandomAccessFile(tempKnownFile, "rws")) {
-                mEverStoredApps.remove(packageName);
-                for (String s : mEverStoredApps) {
-                    known.writeUTF(s);
-                    if (MORE_DEBUG) Slog.v(TAG, "    " + s);
-                }
-                known.close();
-                if (!tempKnownFile.renameTo(mEverStored)) {
-                    throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
-                }
-            } catch (IOException e) {
-                // Bad: we couldn't create the new copy.  For safety's sake we
-                // abandon the whole process and remove all what's-backed-up
-                // state entirely, meaning we'll force a backup pass for every
-                // participant on the next boot or [re]install.
-                Slog.w(TAG, "Error rewriting " + mEverStored, e);
-                mEverStoredApps.clear();
-                tempKnownFile.delete();
-                mEverStored.delete();
-            }
-        }
+        mAppsBackedUpOnThisDeviceJournal.addPackage(packageName);
     }
 
     // Persistently record the current and ancestral backup tokens as well
@@ -1591,7 +1500,7 @@
 
         long token = mAncestralToken;
         synchronized (mQueueLock) {
-            if (mEverStoredApps.contains(packageName)) {
+            if (mAppsBackedUpOnThisDeviceJournal.hasBeenProcessed(packageName)) {
                 if (MORE_DEBUG) {
                     Slog.i(TAG, "App in ever-stored, so using current token");
                 }
@@ -3394,8 +3303,9 @@
                 }
             }
 
-            pw.println("Ever backed up: " + mEverStoredApps.size());
-            for (String pkg : mEverStoredApps) {
+            HashSet<String> processedApps = mAppsBackedUpOnThisDeviceJournal.getPackagesCopy();
+            pw.println("Ever backed up: " + processedApps.size());
+            for (String pkg : processedApps) {
                 pw.println("    " + pkg);
             }
 
diff --git a/services/core/Android.mk b/services/core/Android.mk
index c90b402..5e188e0 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -27,13 +27,13 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     time_zone_distro \
     time_zone_distro_installer \
-    android.hidl.base-V1.0-java-static \
-    android.hardware.weaver-V1.0-java-static \
-    android.hardware.biometrics.fingerprint-V2.1-java-static \
-    android.hardware.oemlock-V1.0-java-static \
-    android.hardware.tetheroffload.control-V1.0-java-static \
-    android.hardware.vibrator-V1.0-java-constants \
-    android.hardware.configstore-V1.0-java-static
+    android.hidl.base-V1.0-java \
+    android.hardware.weaver-V1.0-java \
+    android.hardware.biometrics.fingerprint-V2.1-java \
+    android.hardware.oemlock-V1.0-java \
+    android.hardware.tetheroffload.control-V1.0-java \
+    android.hardware.vibrator-V1.0-java \
+    android.hardware.configstore-V1.0-java
 
 ifneq ($(INCREMENTAL_BUILDS),)
     LOCAL_PROGUARD_ENABLED := disabled
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 29f8a11..50b8df2 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -52,6 +52,7 @@
 import android.util.TimeUtils;
 import android.util.Xml;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.os.Zygote;
@@ -87,6 +88,11 @@
     static final String TAG = "AppOps";
     static final boolean DEBUG = false;
 
+    private static final int NO_VERSION = -1;
+    /** Increment by one every time and add the corresponding upgrade logic in
+     *  {@link #upgradeLocked(int)} below. The first version was 1 */
+    private static final int CURRENT_VERSION = 1;
+
     // Write at most every 30 minutes.
     static final long WRITE_DELAY = DEBUG ? 1000 : 30*60*1000;
 
@@ -112,14 +118,16 @@
         }
     };
 
-    private final SparseArray<UidState> mUidStates = new SparseArray<>();
+    @VisibleForTesting
+    final SparseArray<UidState> mUidStates = new SparseArray<>();
 
     /*
      * These are app op restrictions imposed per user from various parties.
      */
     private final ArrayMap<IBinder, ClientRestrictionState> mOpUserRestrictions = new ArrayMap<>();
 
-    private static final class UidState {
+    @VisibleForTesting
+    static final class UidState {
         public final int uid;
         public ArrayMap<String, Ops> pkgOps;
         public SparseIntArray opModes;
@@ -1398,6 +1406,7 @@
     }
 
     void readState() {
+        int oldVersion = NO_VERSION;
         synchronized (mFile) {
             synchronized (this) {
                 FileInputStream stream;
@@ -1422,6 +1431,11 @@
                         throw new IllegalStateException("no start tag found");
                     }
 
+                    final String versionString = parser.getAttributeValue(null, "v");
+                    if (versionString != null) {
+                        oldVersion = Integer.parseInt(versionString);
+                    }
+
                     int outerDepth = parser.getDepth();
                     while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                             && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
@@ -1464,6 +1478,55 @@
                 }
             }
         }
+        synchronized (this) {
+            upgradeLocked(oldVersion);
+        }
+    }
+
+    private void upgradeRunAnyInBackgroundLocked() {
+        for (int i = 0; i < mUidStates.size(); i++) {
+            final UidState uidState = mUidStates.valueAt(i);
+            if (uidState == null) {
+                continue;
+            }
+            if (uidState.opModes != null) {
+                final int idx = uidState.opModes.indexOfKey(AppOpsManager.OP_RUN_IN_BACKGROUND);
+                if (idx >= 0) {
+                    uidState.opModes.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
+                            uidState.opModes.valueAt(idx));
+                }
+            }
+            if (uidState.pkgOps == null) {
+                continue;
+            }
+            for (int j = 0; j < uidState.pkgOps.size(); j++) {
+                Ops ops = uidState.pkgOps.valueAt(j);
+                if (ops != null) {
+                    final Op op = ops.get(AppOpsManager.OP_RUN_IN_BACKGROUND);
+                    if (op != null && op.mode != AppOpsManager.opToDefaultMode(op.op)) {
+                        final Op copy = new Op(op.uid, op.packageName,
+                                AppOpsManager.OP_RUN_ANY_IN_BACKGROUND);
+                        copy.mode = op.mode;
+                        ops.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, copy);
+                    }
+                }
+            }
+        }
+    }
+
+    private void upgradeLocked(int oldVersion) {
+        if (oldVersion >= CURRENT_VERSION) {
+            return;
+        }
+        Slog.d(TAG, "Upgrading app-ops xml from version " + oldVersion + " to " + CURRENT_VERSION);
+        switch (oldVersion) {
+            case NO_VERSION:
+                upgradeRunAnyInBackgroundLocked();
+                // fall through
+            case 1:
+                // for future upgrades
+        }
+        scheduleFastWriteLocked();
     }
 
     void readUidOps(XmlPullParser parser) throws NumberFormatException,
@@ -1613,6 +1676,7 @@
                 out.setOutput(stream, StandardCharsets.UTF_8.name());
                 out.startDocument(null, true);
                 out.startTag(null, "app-ops");
+                out.attribute(null, "v", String.valueOf(CURRENT_VERSION));
 
                 final int uidStateCount = mUidStates.size();
                 for (int i = 0; i < uidStateCount; i++) {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 814e4be..c23757f 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -214,8 +214,7 @@
             Context.BIND_AUTO_CREATE
             | Context.BIND_NOT_VISIBLE
             | Context.BIND_NOT_FOREGROUND
-            | Context.BIND_IMPORTANT_BACKGROUND
-            | Context.BIND_SHOWING_UI;
+            | Context.BIND_IMPORTANT_BACKGROUND;
 
     /**
      * Binding flags used only while the {@link InputMethodService} is showing window.
@@ -223,7 +222,8 @@
     private static final int IME_VISIBLE_BIND_FLAGS =
             Context.BIND_AUTO_CREATE
             | Context.BIND_TREAT_LIKE_ACTIVITY
-            | Context.BIND_FOREGROUND_SERVICE;
+            | Context.BIND_FOREGROUND_SERVICE
+            | Context.BIND_SHOWING_UI;
 
     @Retention(SOURCE)
     @IntDef({HardKeyboardBehavior.WIRELESS_AFFORDANCE, HardKeyboardBehavior.WIRED_AFFORDANCE})
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index fdc0bba..d60df83 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -27,6 +27,7 @@
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.database.ContentObserver;
+import android.location.LocationManager;
 import android.net.INetworkRecommendationProvider;
 import android.net.INetworkScoreCache;
 import android.net.INetworkScoreService;
@@ -113,6 +114,16 @@
         }
     };
 
+    private BroadcastReceiver mLocationModeReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            if (LocationManager.MODE_CHANGED_ACTION.equals(action)) {
+                refreshBinding();
+            }
+        }
+    };
+
     /**
      * Clears scores when the active scorer package is no longer valid and
      * manages the service connection.
@@ -241,6 +252,10 @@
                 mUserIntentReceiver, UserHandle.SYSTEM, filter, null /* broadcastPermission*/,
                 null /* scheduler */);
         mHandler = new ServiceHandler(looper);
+        IntentFilter locationModeFilter = new IntentFilter(LocationManager.MODE_CHANGED_ACTION);
+        mContext.registerReceiverAsUser(
+                mLocationModeReceiver, UserHandle.SYSTEM, locationModeFilter,
+                null /* broadcastPermission*/, mHandler);
         mContentObserver = new DispatchingContentObserver(context, mHandler);
         mServiceConnProducer = serviceConnProducer;
     }
diff --git a/services/core/java/com/android/server/NetworkScorerAppManager.java b/services/core/java/com/android/server/NetworkScorerAppManager.java
index 42777bf..bfd4247 100644
--- a/services/core/java/com/android/server/NetworkScorerAppManager.java
+++ b/services/core/java/com/android/server/NetworkScorerAppManager.java
@@ -18,6 +18,7 @@
 
 import android.Manifest.permission;
 import android.annotation.Nullable;
+import android.app.AppOpsManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -79,7 +80,7 @@
         List<NetworkScorerAppData> appDataList = new ArrayList<>();
         for (int i = 0; i < resolveInfos.size(); i++) {
             final ServiceInfo serviceInfo = resolveInfos.get(i).serviceInfo;
-            if (hasPermissions(serviceInfo.packageName)) {
+            if (hasPermissions(serviceInfo.applicationInfo.uid, serviceInfo.packageName)) {
                 if (VERBOSE) {
                     Log.v(TAG, serviceInfo.packageName + " is a valid scorer/recommender.");
                 }
@@ -197,12 +198,33 @@
         return null;
     }
 
-    private boolean hasPermissions(String packageName) {
+    private boolean hasPermissions(final int uid, final String packageName) {
+        return hasScoreNetworksPermission(packageName)
+                && canAccessLocation(uid, packageName);
+    }
+
+    private boolean hasScoreNetworksPermission(String packageName) {
         final PackageManager pm = mContext.getPackageManager();
         return pm.checkPermission(permission.SCORE_NETWORKS, packageName)
                 == PackageManager.PERMISSION_GRANTED;
     }
 
+    private boolean canAccessLocation(int uid, String packageName) {
+        final PackageManager pm = mContext.getPackageManager();
+        final AppOpsManager appOpsManager =
+                (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        return isLocationModeEnabled()
+                && pm.checkPermission(permission.ACCESS_COARSE_LOCATION, packageName)
+                == PackageManager.PERMISSION_GRANTED
+                && appOpsManager.noteOp(AppOpsManager.OP_COARSE_LOCATION, uid, packageName)
+                == AppOpsManager.MODE_ALLOWED;
+    }
+
+    private boolean isLocationModeEnabled() {
+        return mSettingsFacade.getSecureInt(mContext, Settings.Secure.LOCATION_MODE,
+                Settings.Secure.LOCATION_MODE_OFF) != Settings.Secure.LOCATION_MODE_OFF;
+    }
+
     /**
      * Set the specified package as the default scorer application.
      *
@@ -270,23 +292,20 @@
             return;
         }
 
-        // the active scorer isn't valid, revert to the default if it's different
+        int newEnabledSetting = NetworkScoreManager.RECOMMENDATIONS_ENABLED_OFF;
+        // the active scorer isn't valid, revert to the default if it's different and valid
         final String defaultPackageName = getDefaultPackageSetting();
-        if (!TextUtils.equals(currentPackageName, defaultPackageName)) {
-            setNetworkRecommendationsPackage(defaultPackageName);
+        if (!TextUtils.equals(currentPackageName, defaultPackageName)
+                && getScorer(defaultPackageName) != null) {
             if (DEBUG) {
-                Log.d(TAG, "Defaulted the network recommendations app to: " + defaultPackageName);
+                Log.d(TAG, "Defaulting the network recommendations app to: "
+                        + defaultPackageName);
             }
-            if (getScorer(defaultPackageName) != null) { // the default is valid
-                if (DEBUG) Log.d(TAG, defaultPackageName + " is now the active scorer.");
-                setNetworkRecommendationsEnabledSetting(
-                        NetworkScoreManager.RECOMMENDATIONS_ENABLED_ON);
-            } else { // the default isn't valid either, we're disabled at this point
-                if (DEBUG) Log.d(TAG, defaultPackageName + " is not an active scorer.");
-                setNetworkRecommendationsEnabledSetting(
-                        NetworkScoreManager.RECOMMENDATIONS_ENABLED_OFF);
-            }
+            setNetworkRecommendationsPackage(defaultPackageName);
+            newEnabledSetting = NetworkScoreManager.RECOMMENDATIONS_ENABLED_ON;
         }
+
+        setNetworkRecommendationsEnabledSetting(newEnabledSetting);
     }
 
     /**
@@ -352,6 +371,9 @@
     private void setNetworkRecommendationsPackage(String packageName) {
         mSettingsFacade.putString(mContext,
                 Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE, packageName);
+        if (VERBOSE) {
+            Log.d(TAG, Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE + " set to " + packageName);
+        }
     }
 
     private int getNetworkRecommendationsEnabledSetting() {
@@ -361,6 +383,9 @@
     private void setNetworkRecommendationsEnabledSetting(int value) {
         mSettingsFacade.putInt(mContext,
                 Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED, value);
+        if (VERBOSE) {
+            Log.d(TAG, Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED + " set to " + value);
+        }
     }
 
     /**
@@ -382,5 +407,9 @@
         public int getInt(Context context, String name, int defaultValue) {
             return Settings.Global.getInt(context.getContentResolver(), name, defaultValue);
         }
+
+        public int getSecureInt(Context context, String name, int defaultValue) {
+            return Settings.Secure.getInt(context.getContentResolver(), name, defaultValue);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/TextServicesManagerService.java b/services/core/java/com/android/server/TextServicesManagerService.java
index 21e9dca..77cd24a 100644
--- a/services/core/java/com/android/server/TextServicesManagerService.java
+++ b/services/core/java/com/android/server/TextServicesManagerService.java
@@ -591,7 +591,7 @@
             Slog.w(TAG, "bind service: " + info.getId());
         }
         if (!bindCurrentSpellCheckerService(serviceIntent, connection,
-                Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE)) {
+                Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT_BACKGROUND)) {
             Slog.e(TAG, "Failed to get a spell checker service.");
             return null;
         }
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 6a81d32..8d46d1e 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -18,7 +18,11 @@
 
 import android.app.IActivityController;
 import android.os.Binder;
+import android.os.Build;
 import android.os.RemoteException;
+import android.system.ErrnoException;
+import android.system.OsConstants;
+import android.system.StructRlimit;
 import com.android.internal.os.ZygoteConnectionConstants;
 import com.android.server.am.ActivityManagerService;
 
@@ -45,6 +49,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 
@@ -107,6 +112,7 @@
     int mPhonePid;
     IActivityController mController;
     boolean mAllowRestart = true;
+    final OpenFdMonitor mOpenFdMonitor;
 
     /**
      * Used for checking status of handle threads and scheduling monitor callbacks.
@@ -269,6 +275,8 @@
         // Initialize monitor for Binder threads.
         addMonitor(new BinderThreadMonitor());
 
+        mOpenFdMonitor = OpenFdMonitor.create();
+
         // See the notes on DEFAULT_TIMEOUT.
         assert DB ||
                 DEFAULT_TIMEOUT > ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
@@ -358,7 +366,7 @@
         return checkers;
     }
 
-    private String describeCheckersLocked(ArrayList<HandlerChecker> checkers) {
+    private String describeCheckersLocked(List<HandlerChecker> checkers) {
         StringBuilder builder = new StringBuilder(128);
         for (int i=0; i<checkers.size(); i++) {
             if (builder.length() > 0) {
@@ -410,7 +418,7 @@
     public void run() {
         boolean waitedHalf = false;
         while (true) {
-            final ArrayList<HandlerChecker> blockedCheckers;
+            final List<HandlerChecker> blockedCheckers;
             final String subject;
             final boolean allowRestart;
             int debuggerWasConnected = 0;
@@ -447,30 +455,40 @@
                     timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
                 }
 
-                final int waitState = evaluateCheckerCompletionLocked();
-                if (waitState == COMPLETED) {
-                    // The monitors have returned; reset
-                    waitedHalf = false;
-                    continue;
-                } else if (waitState == WAITING) {
-                    // still waiting but within their configured intervals; back off and recheck
-                    continue;
-                } else if (waitState == WAITED_HALF) {
-                    if (!waitedHalf) {
-                        // We've waited half the deadlock-detection interval.  Pull a stack
-                        // trace and wait another half.
-                        ArrayList<Integer> pids = new ArrayList<Integer>();
-                        pids.add(Process.myPid());
-                        ActivityManagerService.dumpStackTraces(true, pids, null, null,
-                            getInterestingNativePids());
-                        waitedHalf = true;
-                    }
-                    continue;
+                boolean fdLimitTriggered = false;
+                if (mOpenFdMonitor != null) {
+                    fdLimitTriggered = mOpenFdMonitor.monitor();
                 }
 
-                // something is overdue!
-                blockedCheckers = getBlockedCheckersLocked();
-                subject = describeCheckersLocked(blockedCheckers);
+                if (!fdLimitTriggered) {
+                    final int waitState = evaluateCheckerCompletionLocked();
+                    if (waitState == COMPLETED) {
+                        // The monitors have returned; reset
+                        waitedHalf = false;
+                        continue;
+                    } else if (waitState == WAITING) {
+                        // still waiting but within their configured intervals; back off and recheck
+                        continue;
+                    } else if (waitState == WAITED_HALF) {
+                        if (!waitedHalf) {
+                            // We've waited half the deadlock-detection interval.  Pull a stack
+                            // trace and wait another half.
+                            ArrayList<Integer> pids = new ArrayList<Integer>();
+                            pids.add(Process.myPid());
+                            ActivityManagerService.dumpStackTraces(true, pids, null, null,
+                                getInterestingNativePids());
+                            waitedHalf = true;
+                        }
+                        continue;
+                    }
+
+                    // something is overdue!
+                    blockedCheckers = getBlockedCheckersLocked();
+                    subject = describeCheckersLocked(blockedCheckers);
+                } else {
+                    blockedCheckers = Collections.emptyList();
+                    subject = "Open FD high water mark reached";
+                }
                 allowRestart = mAllowRestart;
             }
 
@@ -584,4 +602,87 @@
     }
 
     private native void native_dumpKernelStacks(String tracesPath);
+
+    public static final class OpenFdMonitor {
+        /**
+         * Number of FDs below the soft limit that we trigger a runtime restart at. This was
+         * chosen arbitrarily, but will need to be at least 6 in order to have a sufficient number
+         * of FDs in reserve to complete a dump.
+         */
+        private static final int FD_HIGH_WATER_MARK = 12;
+
+        private final File mDumpDir;
+        private final File mFdHighWaterMark;
+
+        public static OpenFdMonitor create() {
+            // Only run the FD monitor on debuggable builds (such as userdebug and eng builds).
+            if (!Build.IS_DEBUGGABLE) {
+                return null;
+            }
+
+            // Don't run the FD monitor on builds that have a global ANR trace file. We're using
+            // the ANR trace directory as a quick hack in order to get these traces in bugreports
+            // and we wouldn't want to overwrite something important.
+            final String dumpDirStr = SystemProperties.get("dalvik.vm.stack-trace-dir", "");
+            if (dumpDirStr.isEmpty()) {
+                return null;
+            }
+
+            final StructRlimit rlimit;
+            try {
+                rlimit = android.system.Os.getrlimit(OsConstants.RLIMIT_NOFILE);
+            } catch (ErrnoException errno) {
+                Slog.w(TAG, "Error thrown from getrlimit(RLIMIT_NOFILE)", errno);
+                return null;
+            }
+
+            // The assumption we're making here is that FD numbers are allocated (more or less)
+            // sequentially, which is currently (and historically) true since open is currently
+            // specified to always return the lowest-numbered non-open file descriptor for the
+            // current process.
+            //
+            // We do this to avoid having to enumerate the contents of /proc/self/fd in order to
+            // count the number of descriptors open in the process.
+            final File fdThreshold = new File("/proc/self/fd/" + (rlimit.rlim_cur - FD_HIGH_WATER_MARK));
+            return new OpenFdMonitor(new File(dumpDirStr), fdThreshold);
+        }
+
+        OpenFdMonitor(File dumpDir, File fdThreshold) {
+            mDumpDir = dumpDir;
+            mFdHighWaterMark = fdThreshold;
+        }
+
+        private void dumpOpenDescriptors() {
+            try {
+                File dumpFile = File.createTempFile("anr_fd_", "", mDumpDir);
+                java.lang.Process proc = new ProcessBuilder()
+                    .command("/system/bin/lsof", "-p", String.valueOf(Process.myPid()))
+                    .redirectErrorStream(true)
+                    .redirectOutput(dumpFile)
+                    .start();
+
+                int returnCode = proc.waitFor();
+                if (returnCode != 0) {
+                    Slog.w(TAG, "Unable to dump open descriptors, lsof return code: "
+                        + returnCode);
+                    dumpFile.delete();
+                }
+            } catch (IOException | InterruptedException ex) {
+                Slog.w(TAG, "Unable to dump open descriptors: " + ex);
+            }
+        }
+
+        /**
+         * @return {@code true} if the high water mark was breached and a dump was written,
+         *     {@code false} otherwise.
+         */
+        public boolean monitor() {
+            if (mFdHighWaterMark.exists()) {
+                dumpOpenDescriptors();
+                return true;
+            }
+
+            return false;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4455f6a..47f3ec6 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -714,7 +714,9 @@
 
     public boolean canShowErrorDialogs() {
         return mShowDialogs && !mSleeping && !mShuttingDown
-                && !mKeyguardController.isKeyguardShowing();
+                && !mKeyguardController.isKeyguardShowing()
+                && !(UserManager.isDeviceInDemoMode(mContext)
+                        && mUserController.getCurrentUser().isDemo());
     }
 
     private static ThreadPriorityBooster sThreadPriorityBooster = new ThreadPriorityBooster(
@@ -13326,7 +13328,6 @@
                 final ActivityRecord r = ActivityRecord.isInStackLocked(token);
                 if (r != null) {
                     final ActivityOptions activityOptions = r.pendingOptions;
-                    r.pendingOptions = null;
                     return activityOptions == null ? null : activityOptions.toBundle();
                 }
                 return null;
@@ -14984,7 +14985,7 @@
                 }
             } else if ("starter".equals(cmd)) {
                 synchronized (this) {
-                    dumpActivityStarterLocked(pw);
+                    dumpActivityStarterLocked(pw, dumpPackage);
                 }
             } else if ("recents".equals(cmd) || "r".equals(cmd)) {
                 synchronized (this) {
@@ -15219,7 +15220,7 @@
                 if (dumpAll) {
                     pw.println("-------------------------------------------------------------------------------");
                 }
-                dumpActivityStarterLocked(pw);
+                dumpActivityStarterLocked(pw, dumpPackage);
                 pw.println();
                 if (dumpAll) {
                     pw.println("-------------------------------------------------------------------------------");
@@ -15289,7 +15290,7 @@
                 if (dumpAll) {
                     pw.println("-------------------------------------------------------------------------------");
                 }
-                dumpActivityStarterLocked(pw);
+                dumpActivityStarterLocked(pw, dumpPackage);
                 pw.println();
                 if (dumpAll) {
                     pw.println("-------------------------------------------------------------------------------");
@@ -15313,7 +15314,7 @@
     }
 
     private void dumpLastANRLocked(PrintWriter pw) {
-        pw.println("ACTIVITY MANAGER ACTIVITIES (dumpsys activity lastanr)");
+        pw.println("ACTIVITY MANAGER LAST ANR (dumpsys activity lastanr)");
         if (mLastANRState == null) {
             pw.println("  <no ANR has occurred since boot>");
         } else {
@@ -15321,9 +15322,9 @@
         }
     }
 
-    private void dumpActivityStarterLocked(PrintWriter pw) {
-        pw.println("ACTIVITY MANAGER ACTIVITIES (dumpsys activity starter)");
-        mActivityStarter.dump(pw, "");
+    private void dumpActivityStarterLocked(PrintWriter pw, String dumpPackage) {
+        pw.println("ACTIVITY MANAGER STARTER (dumpsys activity starter)");
+        mActivityStarter.dump(pw, "", dumpPackage);
     }
 
     void dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args,
@@ -22825,11 +22826,11 @@
                     uidRec.lastBackgroundTime = 0;
                 }
                 final boolean wasCached = uidRec.setProcState
-                        > ActivityManager.PROCESS_STATE_RECEIVER && uidRec.setProcState
-                        != ActivityManager.PROCESS_STATE_NONEXISTENT;
+                        > ActivityManager.PROCESS_STATE_RECEIVER;
                 final boolean isCached = uidRec.curProcState
                         > ActivityManager.PROCESS_STATE_RECEIVER;
-                if (wasCached != isCached) {
+                if (wasCached != isCached ||
+                        uidRec.setProcState == ActivityManager.PROCESS_STATE_NONEXISTENT) {
                     uidChange |= isCached ? UidRecord.CHANGE_CACHED : UidRecord.CHANGE_UNCACHED;
                 }
                 uidRec.setProcState = uidRec.curProcState;
@@ -24090,7 +24091,7 @@
                     pw.println("  Reason: " + reason);
                 }
                 pw.println();
-                mActivityStarter.dump(pw, "  ");
+                mActivityStarter.dump(pw, "  ", null);
                 pw.println();
                 pw.println("-------------------------------------------------------------------------------");
                 dumpActivitiesLocked(null /* fd */, pw, null /* args */, 0 /* opti */,
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index ec20c04..7a19cc3 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -427,11 +427,11 @@
                                 pw.print("\"");
                         pw.print(" primaryColor=");
                         pw.println(Integer.toHexString(taskDescription.getPrimaryColor()));
-                        pw.print(prefix + " backgroundColor=");
+                        pw.print(prefix); pw.print("  backgroundColor=");
                         pw.println(Integer.toHexString(taskDescription.getBackgroundColor()));
-                        pw.print(prefix + " statusBarColor=");
+                        pw.print(prefix); pw.print("  statusBarColor=");
                         pw.println(Integer.toHexString(taskDescription.getStatusBarColor()));
-                        pw.print(prefix + " navigationBarColor=");
+                        pw.print(prefix); pw.print("  navigationBarColor=");
                         pw.println(Integer.toHexString(taskDescription.getNavigationBarColor()));
             }
             if (iconFilename == null && taskDescription.getIcon() != null) {
@@ -2161,7 +2161,7 @@
         if (mStartingWindowState == STARTING_WINDOW_SHOWN && behindFullscreenActivity) {
             if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this);
             mStartingWindowState = STARTING_WINDOW_REMOVED;
-            mWindowContainerController.removeHiddenStartingWindow();
+            mWindowContainerController.removeStartingWindow();
         }
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 9925ba0..79448ca 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -650,6 +650,13 @@
         return topRunningActivityLocked(false /* focusableOnly */);
     }
 
+    void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) {
+        outActivities.clear();
+        for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
+            mTaskHistory.get(taskNdx).getAllRunningVisibleActivitiesLocked(outActivities);
+        }
+    }
+
     private ActivityRecord topRunningActivityLocked(boolean focusableOnly) {
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             ActivityRecord r = mTaskHistory.get(taskNdx).topRunningActivityLocked();
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 5f42cdb..e8bc68f 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -441,6 +441,8 @@
 
     final ActivityMetricsLogger mActivityMetricsLogger;
 
+    private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>();
+
     @Override
     protected int getChildCount() {
         return mActivityDisplays.size();
@@ -954,17 +956,21 @@
                 if (!isFocusedStack(stack)) {
                     continue;
                 }
-                ActivityRecord hr = stack.topRunningActivityLocked();
-                if (hr != null) {
-                    if (hr.app == null && app.uid == hr.info.applicationInfo.uid
-                            && processName.equals(hr.processName)) {
+                stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
+                final ActivityRecord top = stack.topRunningActivityLocked();
+                final int size = mTmpActivityList.size();
+                for (int i = 0; i < size; i++) {
+                    final ActivityRecord activity = mTmpActivityList.get(i);
+                    if (activity.app == null && app.uid == activity.info.applicationInfo.uid
+                            && processName.equals(activity.processName)) {
                         try {
-                            if (realStartActivityLocked(hr, app, true, true)) {
+                            if (realStartActivityLocked(activity, app,
+                                    top == activity /* andResume */, true /* checkConfig */)) {
                                 didSomething = true;
                             }
                         } catch (RemoteException e) {
                             Slog.w(TAG, "Exception in new application when starting activity "
-                                  + hr.intent.getComponent().flattenToShortString(), e);
+                                    + top.intent.getComponent().flattenToShortString(), e);
                             throw e;
                         }
                     }
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index b91c7b1..6684f25 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -29,10 +29,10 @@
 import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
 import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
 
-import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.KeyguardManager;
 import android.app.admin.DevicePolicyManagerInternal;
+import android.content.Context;
 import android.content.IIntentSender;
 import android.content.Intent;
 import android.content.IntentSender;
@@ -43,18 +43,26 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.UnlaunchableAppActivity;
 import com.android.server.LocalServices;
 
 /**
  * A class that contains activity intercepting logic for {@link ActivityStarter#startActivityLocked}
- * It's initialized
+ * It's initialized via setStates and interception occurs via the intercept method.
+ *
+ * Note that this class is instantiated when {@link ActivityManagerService} gets created so there
+ * is no guarantee that other system services are already present.
  */
 class ActivityStartInterceptor {
 
     private final ActivityManagerService mService;
-    private UserManager mUserManager;
     private final ActivityStackSupervisor mSupervisor;
+    private final Context mServiceContext;
+    private final UserController mUserController;
+
+    // UserManager cannot be final as it's not ready when this class is instantiated during boot
+    private UserManager mUserManager;
 
     /*
      * Per-intent states loaded from ActivityStarter than shouldn't be changed by any
@@ -69,7 +77,8 @@
     /*
      * Per-intent states that were load from ActivityStarter and are subject to modifications
      * by the interception routines. After calling {@link #intercept} the caller should assign
-     * these values back to {@link ActivityStarter#startActivityLocked}'s local variables.
+     * these values back to {@link ActivityStarter#startActivityLocked}'s local variables if
+     * {@link #intercept} returns true.
      */
     Intent mIntent;
     int mCallingPid;
@@ -81,10 +90,22 @@
     ActivityOptions mActivityOptions;
 
     ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor) {
-        mService = service;
-        mSupervisor = supervisor;
+        this(service, supervisor, service.mContext, service.mUserController);
     }
 
+    @VisibleForTesting
+    ActivityStartInterceptor(ActivityManagerService service, ActivityStackSupervisor supervisor,
+            Context context, UserController userController) {
+        mService = service;
+        mSupervisor = supervisor;
+        mServiceContext = context;
+        mUserController = userController;
+    }
+
+    /**
+     * Effectively initialize the class before intercepting the start intent. The values set in this
+     * method should not be changed during intercept.
+     */
     void setStates(int userId, int realCallingPid, int realCallingUid, int startFlags,
             String callingPackage) {
         mRealCallingPid = realCallingPid;
@@ -94,9 +115,16 @@
         mCallingPackage = callingPackage;
     }
 
-    void intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
+    /**
+     * Intercept the launch intent based on various signals. If an interception happened the
+     * internal variables get assigned and need to be read explicitly by the caller.
+     *
+     * @return true if an interception occurred
+     */
+    boolean intercept(Intent intent, ResolveInfo rInfo, ActivityInfo aInfo, String resolvedType,
             TaskRecord inTask, int callingPid, int callingUid, ActivityOptions activityOptions) {
-        mUserManager = UserManager.get(mService.mContext);
+        mUserManager = UserManager.get(mServiceContext);
+
         mIntent = intent;
         mCallingPid = callingPid;
         mCallingUid = callingUid;
@@ -105,17 +133,18 @@
         mResolvedType = resolvedType;
         mInTask = inTask;
         mActivityOptions = activityOptions;
+
         if (interceptSuspendPackageIfNeed()) {
             // Skip the rest of interceptions as the package is suspended by device admin so
             // no user action can undo this.
-            return;
+            return true;
         }
         if (interceptQuietProfileIfNeeded()) {
             // If work profile is turned off, skip the work challenge since the profile can only
             // be unlocked when profile's user is running.
-            return;
+            return true;
         }
-        interceptWorkProfileChallengeIfNeeded();
+        return interceptWorkProfileChallengeIfNeeded();
     }
 
     private boolean interceptQuietProfileIfNeeded() {
@@ -146,8 +175,8 @@
                 (mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) {
             return false;
         }
-        DevicePolicyManagerInternal devicePolicyManager = LocalServices.getService(
-                DevicePolicyManagerInternal.class);
+        DevicePolicyManagerInternal devicePolicyManager = LocalServices
+                .getService(DevicePolicyManagerInternal.class);
         if (devicePolicyManager == null) {
             return false;
         }
@@ -207,7 +236,7 @@
      */
     private Intent interceptWithConfirmCredentialsIfNeeded(Intent intent, String resolvedType,
             ActivityInfo aInfo, String callingPackage, int userId) {
-        if (!mService.mUserController.shouldConfirmCredentials(userId)) {
+        if (!mUserController.shouldConfirmCredentials(userId)) {
             return null;
         }
         // TODO(b/28935539): should allow certain activities to bypass work challenge
@@ -216,7 +245,7 @@
                 Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
                 new String[]{ resolvedType },
                 FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE, null);
-        final KeyguardManager km = (KeyguardManager) mService.mContext
+        final KeyguardManager km = (KeyguardManager) mServiceContext
                 .getSystemService(KEYGUARD_SERVICE);
         final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId);
         if (newIntent == null) {
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 0931587..462a677 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -144,7 +144,7 @@
 
     private final ActivityManagerService mService;
     private final ActivityStackSupervisor mSupervisor;
-    private ActivityStartInterceptor mInterceptor;
+    private final ActivityStartInterceptor mInterceptor;
     private WindowManagerService mWindowManager;
 
     final ArrayList<PendingActivityLaunch> mPendingActivityLaunches = new ArrayList<>();
@@ -446,16 +446,20 @@
         }
 
         mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage);
-        mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid, callingUid,
-                options);
-        intent = mInterceptor.mIntent;
-        rInfo = mInterceptor.mRInfo;
-        aInfo = mInterceptor.mAInfo;
-        resolvedType = mInterceptor.mResolvedType;
-        inTask = mInterceptor.mInTask;
-        callingPid = mInterceptor.mCallingPid;
-        callingUid = mInterceptor.mCallingUid;
-        options = mInterceptor.mActivityOptions;
+        if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, callingPid,
+                callingUid, options)) {
+            // activity start was intercepted, e.g. because the target user is currently in quiet
+            // mode (turn off work) or the target application is suspended
+            intent = mInterceptor.mIntent;
+            rInfo = mInterceptor.mRInfo;
+            aInfo = mInterceptor.mAInfo;
+            resolvedType = mInterceptor.mResolvedType;
+            inTask = mInterceptor.mInTask;
+            callingPid = mInterceptor.mCallingPid;
+            callingUid = mInterceptor.mCallingUid;
+            options = mInterceptor.mActivityOptions;
+        }
+
         if (abort) {
             if (resultRecord != null) {
                 resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
@@ -2306,40 +2310,76 @@
         return didSomething;
     }
 
-    void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + "ActivityStarter:");
+    void dump(PrintWriter pw, String prefix, String dumpPackage) {
         prefix = prefix + "  ";
 
-        pw.println(prefix + "mCurrentUser=" + mSupervisor.mCurrentUser);
-        pw.println(prefix + "mLastStartReason=" + mLastStartReason);
-        pw.println(prefix + "mLastStartActivityTimeMs="
-                + DateFormat.getDateTimeInstance().format(new Date(mLastStartActivityTimeMs)));
-        pw.println(prefix + "mLastStartActivityResult=" + mLastStartActivityResult);
+        if (dumpPackage != null) {
+            if ((mLastStartActivityRecord[0] == null ||
+                    !dumpPackage.equals(mLastHomeActivityStartRecord[0].packageName)) &&
+                    (mLastHomeActivityStartRecord[0] == null ||
+                    !dumpPackage.equals(mLastHomeActivityStartRecord[0].packageName)) &&
+                    (mStartActivity == null || !dumpPackage.equals(mStartActivity.packageName))) {
+                pw.print(prefix);
+                pw.println("(nothing)");
+                return;
+            }
+        }
+
+        pw.print(prefix);
+        pw.print("mCurrentUser=");
+        pw.println(mSupervisor.mCurrentUser);
+        pw.print(prefix);
+        pw.print("mLastStartReason=");
+        pw.println(mLastStartReason);
+        pw.print(prefix);
+        pw.print("mLastStartActivityTimeMs=");
+        pw.println(DateFormat.getDateTimeInstance().format(new Date(mLastStartActivityTimeMs)));
+        pw.print(prefix);
+        pw.print("mLastStartActivityResult=");
+        pw.println(mLastStartActivityResult);
         ActivityRecord r = mLastStartActivityRecord[0];
         if (r != null) {
-            pw.println(prefix + "mLastStartActivityRecord:");
-            r.dump(pw, prefix + " ");
+            pw.print(prefix);
+            pw.println("mLastStartActivityRecord:");
+            r.dump(pw, prefix + "  ");
         }
-        pw.println(prefix + "mLastHomeActivityStartResult=" + mLastHomeActivityStartResult);
+        pw.print(prefix);
+        pw.print("mLastHomeActivityStartResult=");
+        pw.println(mLastHomeActivityStartResult);
         r = mLastHomeActivityStartRecord[0];
         if (r != null) {
-            pw.println(prefix + "mLastHomeActivityStartRecord:");
-            r.dump(pw, prefix + " ");
+            pw.print(prefix);
+            pw.println("mLastHomeActivityStartRecord:");
+            r.dump(pw, prefix + "  ");
         }
         if (mStartActivity != null) {
-            pw.println(prefix + "mStartActivity:");
-            mStartActivity.dump(pw, prefix + " ");
+            pw.print(prefix);
+            pw.println("mStartActivity:");
+            mStartActivity.dump(pw, prefix + "  ");
         }
         if (mIntent != null) {
-            pw.println(prefix + "mIntent=" + mIntent);
+            pw.print(prefix);
+            pw.print("mIntent=");
+            pw.println(mIntent);
         }
         if (mOptions != null) {
-            pw.println(prefix + "mOptions=" + mOptions);
+            pw.print(prefix);
+            pw.print("mOptions=");
+            pw.println(mOptions);
         }
-        pw.println(prefix + "mLaunchSingleTop=" + mLaunchSingleTop
-                + " mLaunchSingleInstance=" + mLaunchSingleInstance
-                + " mLaunchSingleTask=" + mLaunchSingleTask
-                + " mLaunchFlags=0x" + Integer.toHexString(mLaunchFlags)
-                + " mDoResume=" + mDoResume + " mAddingToTask=" + mAddingToTask);
+        pw.print(prefix);
+        pw.print("mLaunchSingleTop=");
+        pw.print(mLaunchSingleTop);
+        pw.print(" mLaunchSingleInstance=");
+        pw.print(mLaunchSingleInstance);
+        pw.print(" mLaunchSingleTask=");
+        pw.println(mLaunchSingleTask);
+        pw.print(prefix);
+        pw.print("mLaunchFlags=0x");
+        pw.print(Integer.toHexString(mLaunchFlags));
+        pw.print(" mDoResume=");
+        pw.print(mDoResume);
+        pw.print(" mAddingToTask=");
+        pw.println(mAddingToTask);
     }
 }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 5753fbc..88d8944 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1159,6 +1159,17 @@
         return null;
     }
 
+    void getAllRunningVisibleActivitiesLocked(ArrayList<ActivityRecord> outActivities) {
+        if (mStack != null) {
+            for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
+                ActivityRecord r = mActivities.get(activityNdx);
+                if (!r.finishing && r.okToShowLocked() && r.visibleIgnoringKeyguard) {
+                    outActivities.add(r);
+                }
+            }
+        }
+    }
+
     ActivityRecord topRunningActivityWithStartingWindowLocked() {
         if (mStack != null) {
             for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 405ee32..95d7e9f 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -22,9 +22,7 @@
 import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
 import static android.app.ActivityManager.USER_OP_IS_CURRENT;
 import static android.app.ActivityManager.USER_OP_SUCCESS;
-import static android.content.Context.KEYGUARD_SERVICE;
 import static android.os.Process.SYSTEM_UID;
-
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -109,7 +107,7 @@
 /**
  * Helper class for {@link ActivityManagerService} responsible for multi-user functionality.
  */
-final class UserController {
+class UserController {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "UserController" : TAG_AM;
 
     // Maximum number of users we allow to be running at a time.
@@ -671,12 +669,6 @@
         }
 
         if (stopped) {
-            // Evict the user's credential encryption key
-            try {
-                getStorageManager().lockUserKey(userId);
-            } catch (RemoteException re) {
-                throw re.rethrowAsRuntimeException();
-            }
             mInjector.systemServiceManagerCleanupUser(userId);
             synchronized (mLock) {
                 mInjector.getActivityStackSupervisor().removeUserLocked(userId);
@@ -685,6 +677,12 @@
             if (getUserInfo(userId).isEphemeral()) {
                 mInjector.getUserManager().removeUser(userId);
             }
+            // Evict the user's credential encryption key.
+            try {
+                getStorageManager().lockUserKey(userId);
+            } catch (RemoteException re) {
+                throw re.rethrowAsRuntimeException();
+            }
         }
     }
 
@@ -1602,7 +1600,7 @@
      * Returns whether the given user requires credential entry at this time. This is used to
      * intercept activity launches for work apps when the Work Challenge is present.
      */
-    boolean shouldConfirmCredentials(int userId) {
+    protected boolean shouldConfirmCredentials(int userId) {
         synchronized (mLock) {
             if (mStartedUsers.get(userId) == null) {
                 return false;
diff --git a/services/core/java/com/android/server/broadcastradio/Convert.java b/services/core/java/com/android/server/broadcastradio/Convert.java
new file mode 100644
index 0000000..125554f
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/Convert.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright (C) 2017 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.broadcastradio;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Slog;
+
+import java.util.Map;
+import java.util.Set;
+
+class Convert {
+    private static final String TAG = "BroadcastRadioService.Convert";
+
+    /**
+     * Converts string map to an array that's easily accessible by native code.
+     *
+     * Calling this java method once is more efficient than converting map object on the native
+     * side, which requires several separate java calls for each element.
+     *
+     * @param map map to convert.
+     * @returns array (sized the same as map) of two-element string arrays
+     *          (first element is the key, second is value).
+     */
+    static @NonNull String[][] stringMapToNative(@Nullable Map<String, String> map) {
+        if (map == null) {
+            Slog.v(TAG, "map is null, returning zero-elements array");
+            return new String[0][0];
+        }
+
+        Set<Map.Entry<String, String>> entries = map.entrySet();
+        int len = entries.size();
+        String[][] arr = new String[len][2];
+
+        int i = 0;
+        for (Map.Entry<String, String> entry : entries) {
+            arr[i][0] = entry.getKey();
+            arr[i][1] = entry.getValue();
+            i++;
+        }
+
+        Slog.v(TAG, "converted " + i + " element(s)");
+        return arr;
+    }
+}
diff --git a/services/core/java/com/android/server/broadcastradio/Tuner.java b/services/core/java/com/android/server/broadcastradio/Tuner.java
index b9a1e81..06a5af5 100644
--- a/services/core/java/com/android/server/broadcastradio/Tuner.java
+++ b/services/core/java/com/android/server/broadcastradio/Tuner.java
@@ -28,6 +28,7 @@
 import android.util.Slog;
 
 import java.util.List;
+import java.util.Map;
 
 class Tuner extends ITuner.Stub {
     private static final String TAG = "BroadcastRadioService.Tuner";
@@ -86,7 +87,7 @@
     private native RadioManager.ProgramInfo nativeGetProgramInformation(long nativeContext);
     private native boolean nativeStartBackgroundScan(long nativeContext);
     private native List<RadioManager.ProgramInfo> nativeGetProgramList(long nativeContext,
-            String filter);
+            Map<String, String> vendorFilter);
 
     private native byte[] nativeGetImage(long nativeContext, int id);
 
@@ -242,10 +243,11 @@
     }
 
     @Override
-    public List<RadioManager.ProgramInfo> getProgramList(String filter) {
+    public List<RadioManager.ProgramInfo> getProgramList(Map vendorFilter) {
+        Map<String, String> sFilter = vendorFilter;
         synchronized (mLock) {
             checkNotClosedLocked();
-            List<RadioManager.ProgramInfo> list = nativeGetProgramList(mNativeContext, filter);
+            List<RadioManager.ProgramInfo> list = nativeGetProgramList(mNativeContext, sFilter);
             if (list == null) {
                 throw new IllegalStateException("Program list is not ready");
             }
diff --git a/services/core/java/com/android/server/broadcastradio/TunerCallback.java b/services/core/java/com/android/server/broadcastradio/TunerCallback.java
index 25f3775..a87ae8d 100644
--- a/services/core/java/com/android/server/broadcastradio/TunerCallback.java
+++ b/services/core/java/com/android/server/broadcastradio/TunerCallback.java
@@ -86,8 +86,8 @@
     }
 
     @Override
-    public void onProgramInfoChanged() {
-        dispatch(() -> mClientCallback.onProgramInfoChanged());
+    public void onCurrentProgramInfoChanged(RadioManager.ProgramInfo info) {
+        dispatch(() -> mClientCallback.onCurrentProgramInfoChanged(info));
     }
 
     @Override
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index b0be8f7..dfc2db5 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -70,6 +70,9 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.UserManagerInternal;
+import android.os.UserManagerInternal.UserRestrictionsListener;
 import android.provider.Settings;
 import android.telephony.CarrierConfigManager;
 import android.telephony.TelephonyManager;
@@ -89,6 +92,7 @@
 import com.android.internal.util.Protocol;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
+import com.android.server.LocalServices;
 import com.android.server.connectivity.tethering.IControlsTethering;
 import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
 import com.android.server.connectivity.tethering.OffloadController;
@@ -236,6 +240,13 @@
         filter.addDataScheme("file");
         mContext.registerReceiver(mStateReceiver, filter, null, smHandler);
 
+        UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
+
+        // this check is useful only for some unit tests; example: ConnectivityServiceTest
+        if (userManager != null) {
+            userManager.addUserRestrictionsListener(new TetheringUserRestrictionListener(this));
+        }
+
         // load device config info
         updateConfiguration();
     }
@@ -710,6 +721,11 @@
     }
 
     private void showTetheredNotification(int id) {
+        showTetheredNotification(id, true);
+    }
+
+    @VisibleForTesting
+    protected void showTetheredNotification(int id, boolean tetheringOn) {
         NotificationManager notificationManager =
                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
         if (notificationManager == null) {
@@ -746,9 +762,16 @@
                 null, UserHandle.CURRENT);
 
         Resources r = Resources.getSystem();
-        CharSequence title = r.getText(com.android.internal.R.string.tethered_notification_title);
-        CharSequence message = r.getText(com.android.internal.R.string.
-                tethered_notification_message);
+        final CharSequence title;
+        final CharSequence message;
+
+        if (tetheringOn) {
+            title = r.getText(com.android.internal.R.string.tethered_notification_title);
+            message = r.getText(com.android.internal.R.string.tethered_notification_message);
+        } else {
+            title = r.getText(com.android.internal.R.string.disable_tether_notification_title);
+            message = r.getText(com.android.internal.R.string.disable_tether_notification_message);
+        }
 
         if (mTetheredNotificationBuilder == null) {
             mTetheredNotificationBuilder =
@@ -770,7 +793,8 @@
                 mTetheredNotificationBuilder.buildInto(new Notification()), UserHandle.ALL);
     }
 
-    private void clearTetheredNotification() {
+    @VisibleForTesting
+    protected void clearTetheredNotification() {
         NotificationManager notificationManager =
             (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
         if (notificationManager != null && mLastNotificationId != 0) {
@@ -874,6 +898,38 @@
         }
     }
 
+    @VisibleForTesting
+    protected static class TetheringUserRestrictionListener implements UserRestrictionsListener {
+        private final Tethering mWrapper;
+
+        public TetheringUserRestrictionListener(Tethering wrapper) {
+            mWrapper = wrapper;
+        }
+
+        public void onUserRestrictionsChanged(int userId,
+                                              Bundle newRestrictions,
+                                              Bundle prevRestrictions) {
+            final boolean newlyDisallowed =
+                    newRestrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING);
+            final boolean previouslyDisallowed =
+                    prevRestrictions.getBoolean(UserManager.DISALLOW_CONFIG_TETHERING);
+            final boolean tetheringDisallowedChanged = (newlyDisallowed != previouslyDisallowed);
+
+            if (!tetheringDisallowedChanged) {
+                return;
+            }
+
+            mWrapper.clearTetheredNotification();
+            final boolean isTetheringActiveOnDevice = (mWrapper.getTetheredIfaces().length != 0);
+
+            if (newlyDisallowed && isTetheringActiveOnDevice) {
+                mWrapper.showTetheredNotification(
+                        com.android.internal.R.drawable.stat_sys_tether_general, false);
+                mWrapper.untetherAll();
+            }
+        }
+    }
+
     private void disableWifiIpServingLocked(String ifname, int apState) {
         mLog.log("Canceling WiFi tethering request - AP_STATE=" + apState);
 
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index a50ec49f..81bccdc 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -851,6 +851,7 @@
                 mSystemAudioActivated = on;
                 mService.announceSystemAudioModeChange(on);
             }
+            startArcAction(on);
         }
     }
 
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index e25f3e6..f827991 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -80,6 +80,7 @@
 import com.android.server.LocalServices;
 import com.android.server.job.JobStore.JobStatusFunctor;
 import com.android.server.job.controllers.AppIdleController;
+import com.android.server.job.controllers.BackgroundJobsController;
 import com.android.server.job.controllers.BatteryController;
 import com.android.server.job.controllers.ConnectivityController;
 import com.android.server.job.controllers.ContentObserverController;
@@ -140,6 +141,8 @@
     BatteryController mBatteryController;
     /** Need direct access to this for testing. */
     StorageController mStorageController;
+    /** Need directly for sending uid state changes */
+    private BackgroundJobsController mBackgroundJobsController;
     /**
      * Queue of pending jobs. The JobServiceContext class will receive jobs from this list
      * when ready to execute them.
@@ -225,6 +228,7 @@
         private static final String KEY_MAX_WORK_RESCHEDULE_COUNT = "max_work_reschedule_count";
         private static final String KEY_MIN_LINEAR_BACKOFF_TIME = "min_linear_backoff_time";
         private static final String KEY_MIN_EXP_BACKOFF_TIME = "min_exp_backoff_time";
+        private static final String KEY_BG_JOBS_RESTRICTED = "bg_jobs_restricted";
 
         private static final int DEFAULT_MIN_IDLE_COUNT = 1;
         private static final int DEFAULT_MIN_CHARGING_COUNT = 1;
@@ -240,6 +244,7 @@
         private static final int DEFAULT_BG_MODERATE_JOB_COUNT = 4;
         private static final int DEFAULT_BG_LOW_JOB_COUNT = 1;
         private static final int DEFAULT_BG_CRITICAL_JOB_COUNT = 1;
+        private static final boolean DEFAULT_BG_JOBS_RESTRICTED = false;
         private static final int DEFAULT_MAX_STANDARD_RESCHEDULE_COUNT = Integer.MAX_VALUE;
         private static final int DEFAULT_MAX_WORK_RESCHEDULE_COUNT = Integer.MAX_VALUE;
         private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
@@ -333,6 +338,11 @@
          */
         long MIN_EXP_BACKOFF_TIME = DEFAULT_MIN_EXP_BACKOFF_TIME;
 
+        /**
+         * Runtime switch for throttling background jobs
+         */
+        boolean BACKGROUND_JOBS_RESTRICTED = DEFAULT_BG_JOBS_RESTRICTED;
+
         private ContentResolver mResolver;
         private final KeyValueListParser mParser = new KeyValueListParser(',');
 
@@ -411,6 +421,12 @@
                         DEFAULT_MIN_LINEAR_BACKOFF_TIME);
                 MIN_EXP_BACKOFF_TIME = mParser.getLong(KEY_MIN_EXP_BACKOFF_TIME,
                         DEFAULT_MIN_EXP_BACKOFF_TIME);
+                final boolean bgJobsRestricted = mParser.getBoolean(KEY_BG_JOBS_RESTRICTED,
+                        DEFAULT_BG_JOBS_RESTRICTED);
+                if (bgJobsRestricted != BACKGROUND_JOBS_RESTRICTED) {
+                    mBackgroundJobsController.enableRestrictionsLocked(
+                            BACKGROUND_JOBS_RESTRICTED = bgJobsRestricted);
+                }
             }
         }
 
@@ -470,6 +486,9 @@
 
             pw.print("    "); pw.print(KEY_MIN_EXP_BACKOFF_TIME); pw.print("=");
             pw.print(MIN_EXP_BACKOFF_TIME); pw.println();
+
+            pw.print("    "); pw.print(KEY_BG_JOBS_RESTRICTED); pw.print("=");
+            pw.print(BACKGROUND_JOBS_RESTRICTED); pw.println();
         }
     }
 
@@ -613,15 +632,24 @@
             if (disabled) {
                 cancelJobsForUid(uid, "uid gone");
             }
+            synchronized (mLock) {
+                mBackgroundJobsController.setUidActiveLocked(uid, false);
+            }
         }
 
         @Override public void onUidActive(int uid) throws RemoteException {
+            synchronized (mLock) {
+                mBackgroundJobsController.setUidActiveLocked(uid, true);
+            }
         }
 
         @Override public void onUidIdle(int uid, boolean disabled) {
             if (disabled) {
                 cancelJobsForUid(uid, "app uid idle");
             }
+            synchronized (mLock) {
+                mBackgroundJobsController.setUidActiveLocked(uid, false);
+            }
         }
 
         @Override public void onUidCachedChanged(int uid, boolean cached) {
@@ -917,6 +945,8 @@
         mControllers.add(mBatteryController);
         mStorageController = StorageController.get(this);
         mControllers.add(mStorageController);
+        mBackgroundJobsController = BackgroundJobsController.get(this);
+        mControllers.add(mBackgroundJobsController);
         mControllers.add(AppIdleController.get(this));
         mControllers.add(ContentObserverController.get(this));
         mControllers.add(DeviceIdleJobsController.get(this));
@@ -997,8 +1027,8 @@
             try {
                 ActivityManager.getService().registerUidObserver(mUidObserver,
                         ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE
-                        | ActivityManager.UID_OBSERVER_IDLE, ActivityManager.PROCESS_STATE_UNKNOWN,
-                        null);
+                        | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE,
+                        ActivityManager.PROCESS_STATE_UNKNOWN, null);
             } catch (RemoteException e) {
                 // ignored; both services live in system_server
             }
diff --git a/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
new file mode 100644
index 0000000..ec50d6d
--- /dev/null
+++ b/services/core/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2017 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.job.controllers;
+
+import android.app.AppOpsManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.IDeviceIdleController;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.ArraySet;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+
+import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.job.JobSchedulerService;
+import com.android.server.job.JobStore;
+
+import java.io.PrintWriter;
+
+public final class BackgroundJobsController extends StateController {
+
+    private static final String LOG_TAG = "BackgroundJobsController";
+    private static final boolean DEBUG = JobSchedulerService.DEBUG;
+
+    // Singleton factory
+    private static final Object sCreationLock = new Object();
+    private static volatile BackgroundJobsController sController;
+
+    /* Runtime switch to keep feature under wraps */
+    private boolean mEnableSwitch;
+    private final JobSchedulerService mJobSchedulerService;
+    private final IAppOpsService mAppOpsService;
+    private final IDeviceIdleController mDeviceIdleController;
+
+    private final SparseBooleanArray mForegroundUids;
+    private int[] mPowerWhitelistedAppIds;
+    private int[] mTempWhitelistedAppIds;
+    /**
+     * Only tracks jobs for which source package app op RUN_ANY_IN_BACKGROUND is not ALLOWED.
+     * Maps jobs to the sourceUid unlike the global {@link JobSchedulerService#mJobs JobStore}
+     * which uses callingUid.
+     */
+    private SparseArray<ArraySet<JobStatus>> mTrackedJobs;
+
+    public static BackgroundJobsController get(JobSchedulerService service) {
+        synchronized (sCreationLock) {
+            if (sController == null) {
+                sController = new BackgroundJobsController(service, service.getContext(),
+                        service.getLock());
+            }
+            return sController;
+        }
+    }
+
+    private BroadcastReceiver mDozeWhitelistReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (mLock) {
+                try {
+                    switch (intent.getAction()) {
+                        case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED:
+                            mPowerWhitelistedAppIds = mDeviceIdleController.getAppIdWhitelist();
+                            break;
+                        case PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED:
+                            mTempWhitelistedAppIds = mDeviceIdleController.getAppIdTempWhitelist();
+                            break;
+                    }
+                } catch (RemoteException rexc) {
+                    Slog.e(LOG_TAG, "Device idle controller not reachable");
+                }
+                if (checkAllTrackedJobsLocked()) {
+                    mStateChangedListener.onControllerStateChanged();
+                }
+            }
+        }
+    };
+
+    private BackgroundJobsController(JobSchedulerService service, Context context, Object lock) {
+        super(service, context, lock);
+        mJobSchedulerService = service;
+        mAppOpsService = IAppOpsService.Stub.asInterface(
+                ServiceManager.getService(Context.APP_OPS_SERVICE));
+        mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+                ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
+
+        mForegroundUids = new SparseBooleanArray();
+        mTrackedJobs = new SparseArray<>();
+        try {
+            mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, null,
+                    new AppOpsWatcher());
+            mPowerWhitelistedAppIds = mDeviceIdleController.getAppIdWhitelist();
+            mTempWhitelistedAppIds = mDeviceIdleController.getAppIdTempWhitelist();
+        } catch (RemoteException rexc) {
+            // Shouldn't happen as they are in the same process.
+            Slog.e(LOG_TAG, "AppOps or DeviceIdle service not reachable", rexc);
+        }
+        IntentFilter powerWhitelistFilter = new IntentFilter();
+        powerWhitelistFilter.addAction(PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED);
+        powerWhitelistFilter.addAction(PowerManager.ACTION_POWER_SAVE_TEMP_WHITELIST_CHANGED);
+        context.registerReceiverAsUser(mDozeWhitelistReceiver, UserHandle.ALL, powerWhitelistFilter,
+                null, null);
+
+        mEnableSwitch = false;
+    }
+
+    @Override
+    public void maybeStartTrackingJobLocked(JobStatus jobStatus, JobStatus lastJob) {
+        final int uid = jobStatus.getSourceUid();
+        final String packageName = jobStatus.getSourcePackageName();
+        try {
+            final int mode = mAppOpsService.checkOperation(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND,
+                    uid, packageName);
+            if (mode == AppOpsManager.MODE_ALLOWED) {
+                jobStatus.setBackgroundNotRestrictedConstraintSatisfied(true);
+                return;
+            }
+        } catch (RemoteException rexc) {
+            Slog.e(LOG_TAG, "Cannot reach app ops service", rexc);
+        }
+        jobStatus.setBackgroundNotRestrictedConstraintSatisfied(canRunJobLocked(uid));
+        startTrackingJobLocked(jobStatus);
+    }
+
+    @Override
+    public void maybeStopTrackingJobLocked(JobStatus jobStatus, JobStatus incomingJob,
+            boolean forUpdate) {
+        stopTrackingJobLocked(jobStatus);
+    }
+
+    /* Called by JobSchedulerService to report uid state changes between active and idle */
+    public void setUidActiveLocked(int uid, boolean active) {
+        final boolean changed = (active != mForegroundUids.get(uid));
+        if (!changed) {
+            return;
+        }
+        if (DEBUG) {
+            Slog.d(LOG_TAG, "uid " + uid + " going to " + (active ? "fg" : "bg"));
+        }
+        if (active) {
+            mForegroundUids.put(uid, true);
+        } else {
+            mForegroundUids.delete(uid);
+        }
+        if (checkTrackedJobsForUidLocked(uid)) {
+            mStateChangedListener.onControllerStateChanged();
+        }
+    }
+
+    @Override
+    public void dumpControllerStateLocked(final PrintWriter pw, final int filterUid) {
+        pw.println("Background restrictions: global switch = " + mEnableSwitch);
+        pw.print("Foreground uids: [");
+        for (int i = 0; i < mForegroundUids.size(); i++) {
+            if (mForegroundUids.valueAt(i)) pw.print(mForegroundUids.keyAt(i) + " ");
+        }
+        pw.println("]");
+        mJobSchedulerService.getJobStore().forEachJob(new JobStore.JobStatusFunctor() {
+            @Override
+            public void process(JobStatus jobStatus) {
+                if (!jobStatus.shouldDump(filterUid)) {
+                    return;
+                }
+                final int uid = jobStatus.getSourceUid();
+                pw.print("  #");
+                jobStatus.printUniqueId(pw);
+                pw.print(" from ");
+                UserHandle.formatUid(pw, uid);
+                pw.print(mForegroundUids.get(uid) ? " foreground" : " background");
+                if (isWhitelistedLocked(uid)) {
+                    pw.print(", whitelisted");
+                }
+                pw.print(": ");
+                pw.print(jobStatus.getSourcePackageName());
+                pw.print(" [background restrictions");
+                final ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
+                pw.print(jobsForUid != null && jobsForUid.contains(jobStatus) ? " on]" : " off]");
+                if ((jobStatus.satisfiedConstraints
+                        & JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0) {
+                    pw.println(" RUNNABLE");
+                } else {
+                    pw.println(" WAITING");
+                }
+            }
+        });
+    }
+
+    public void enableRestrictionsLocked(boolean enable) {
+        mEnableSwitch = enable;
+        Slog.d(LOG_TAG, "Background jobs restrictions switch changed to " + mEnableSwitch);
+        if (checkAllTrackedJobsLocked()) {
+            mStateChangedListener.onControllerStateChanged();
+        }
+    }
+
+    void startTrackingJobLocked(JobStatus jobStatus) {
+        final int uid = jobStatus.getSourceUid();
+        ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
+        if (jobsForUid == null) {
+            jobsForUid = new ArraySet<>();
+            mTrackedJobs.put(uid, jobsForUid);
+        }
+        jobsForUid.add(jobStatus);
+    }
+
+    void stopTrackingJobLocked(JobStatus jobStatus) {
+        final int uid = jobStatus.getSourceUid();
+        ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
+        if (jobsForUid != null) {
+            jobsForUid.remove(jobStatus);
+        }
+    }
+
+    boolean checkAllTrackedJobsLocked() {
+        boolean changed = false;
+        for (int i = 0; i < mTrackedJobs.size(); i++) {
+            changed |= checkTrackedJobsForUidLocked(mTrackedJobs.keyAt(i));
+        }
+        return changed;
+    }
+
+    private boolean checkTrackedJobsForUidLocked(int uid) {
+        final ArraySet<JobStatus> jobsForUid = mTrackedJobs.get(uid);
+        boolean changed = false;
+        if (jobsForUid != null) {
+            for (int i = 0; i < jobsForUid.size(); i++) {
+                JobStatus jobStatus = jobsForUid.valueAt(i);
+                changed |= jobStatus.setBackgroundNotRestrictedConstraintSatisfied(
+                        canRunJobLocked(uid));
+            }
+        }
+        return changed;
+    }
+
+    boolean isWhitelistedLocked(int uid) {
+        return ArrayUtils.contains(mTempWhitelistedAppIds, UserHandle.getAppId(uid))
+                || ArrayUtils.contains(mPowerWhitelistedAppIds, UserHandle.getAppId(uid));
+    }
+
+    boolean canRunJobLocked(int uid) {
+        return !mEnableSwitch || mForegroundUids.get(uid) || isWhitelistedLocked(uid);
+    }
+
+    private final class AppOpsWatcher extends IAppOpsCallback.Stub {
+        @Override
+        public void opChanged(int op, int uid, String packageName) throws RemoteException {
+            synchronized (mLock) {
+                final int mode = mAppOpsService.checkOperation(op, uid, packageName);
+                if (DEBUG) {
+                    Slog.d(LOG_TAG,
+                            "Appop changed for " + uid + ", " + packageName + " to " + mode);
+                }
+                final boolean shouldTrack = (mode != AppOpsManager.MODE_ALLOWED);
+                UpdateTrackedJobsFunc updateTrackedJobs = new UpdateTrackedJobsFunc(uid,
+                        packageName, shouldTrack);
+                mJobSchedulerService.getJobStore().forEachJob(updateTrackedJobs);
+                if (updateTrackedJobs.mChanged) {
+                    mStateChangedListener.onControllerStateChanged();
+                }
+            }
+        }
+    }
+
+    private final class UpdateTrackedJobsFunc implements JobStore.JobStatusFunctor {
+        private final String mPackageName;
+        private final int mUid;
+        private final boolean mShouldTrack;
+        private boolean mChanged = false;
+
+        UpdateTrackedJobsFunc(int uid, String packageName, boolean shouldTrack) {
+            mUid = uid;
+            mPackageName = packageName;
+            mShouldTrack = shouldTrack;
+        }
+
+        @Override
+        public void process(JobStatus jobStatus) {
+            final String packageName = jobStatus.getSourcePackageName();
+            final int uid = jobStatus.getSourceUid();
+            if (mUid != uid || !mPackageName.equals(packageName)) {
+                return;
+            }
+            if (mShouldTrack) {
+                mChanged |= jobStatus.setBackgroundNotRestrictedConstraintSatisfied(
+                        canRunJobLocked(uid));
+                startTrackingJobLocked(jobStatus);
+            } else {
+                mChanged |= jobStatus.setBackgroundNotRestrictedConstraintSatisfied(true);
+                stopTrackingJobLocked(jobStatus);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 303b000..23caa8c 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -70,6 +70,7 @@
     static final int CONSTRAINT_DEVICE_NOT_DOZING = 1<<25;
     static final int CONSTRAINT_NOT_ROAMING = 1<<24;
     static final int CONSTRAINT_METERED = 1<<23;
+    static final int CONSTRAINT_BACKGROUND_NOT_RESTRICTED = 1<<22;
 
     static final int CONNECTIVITY_MASK =
             CONSTRAINT_UNMETERED | CONSTRAINT_CONNECTIVITY |
@@ -707,6 +708,10 @@
         return setConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING, state);
     }
 
+    boolean setBackgroundNotRestrictedConstraintSatisfied(boolean state) {
+        return setConstraintSatisfied(CONSTRAINT_BACKGROUND_NOT_RESTRICTED, state);
+    }
+
     boolean setConstraintSatisfied(int constraint, boolean state) {
         boolean old = (satisfiedConstraints&constraint) != 0;
         if (old == state) {
@@ -758,12 +763,16 @@
         // satisfied).
         // AppNotIdle implicit constraint must be satisfied
         // DeviceNotDozing implicit constraint must be satisfied
+        // NotRestrictedInBackground implicit constraint must be satisfied
         final boolean deadlineSatisfied = (!job.isPeriodic() && hasDeadlineConstraint()
                 && (satisfiedConstraints & CONSTRAINT_DEADLINE) != 0);
         final boolean notIdle = (satisfiedConstraints & CONSTRAINT_APP_NOT_IDLE) != 0;
         final boolean notDozing = (satisfiedConstraints & CONSTRAINT_DEVICE_NOT_DOZING) != 0
                 || (job.getFlags() & JobInfo.FLAG_WILL_BE_FOREGROUND) != 0;
-        return (isConstraintsSatisfied() || deadlineSatisfied) && notIdle && notDozing;
+        final boolean notRestrictedInBg =
+                (satisfiedConstraints & CONSTRAINT_BACKGROUND_NOT_RESTRICTED) != 0;
+        return (isConstraintsSatisfied() || deadlineSatisfied) && notIdle && notDozing
+                && notRestrictedInBg;
     }
 
     static final int CONSTRAINTS_OF_INTEREST =
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index cdf25cf..436ea3c 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -543,7 +543,9 @@
                     loadPropertiesFromResource(context, mProperties);
                     String lpp_profile = mProperties.getProperty("LPP_PROFILE");
                     // set the persist property LPP_PROFILE for the value
-                    SystemProperties.set(LPP_PROFILE, lpp_profile);
+                    if (lpp_profile != null) {
+                        SystemProperties.set(LPP_PROFILE, lpp_profile);
+                    }
                 } else {
                     // reset the persist property
                     SystemProperties.set(LPP_PROFILE, "");
@@ -1052,8 +1054,15 @@
                 // download tasks overrun.
                 synchronized (mLock) {
                     if (mDownloadXtraWakeLock.isHeld()) {
-                        mDownloadXtraWakeLock.release();
-                        if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadXtraData()");
+                        // This wakelock may have time-out, if a timeout was specified.
+                        // Catch (and ignore) any timeout exceptions.
+                        try {
+                            mDownloadXtraWakeLock.release();
+                            if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadXtraData()");
+                        } catch (Exception e) {
+                            Log.i(TAG, "Wakelock timeout & release race exception in "
+                                    + "handleDownloadXtraData()", e);
+                        }
                     } else {
                         Log.e(TAG, "WakeLock expired before release in "
                                 + "handleDownloadXtraData()");
diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS
new file mode 100644
index 0000000..061fd8d
--- /dev/null
+++ b/services/core/java/com/android/server/net/OWNERS
@@ -0,0 +1,10 @@
+set noparent
+
+ek@google.com
+hugobenichi@google.com
+jsharkey@google.com
+lorenzo@google.com
+satk@google.com
+silberst@google.com
+sudheersai@google.com
+yamasani@google.com
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index 207bdba..f7efff0f 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -186,7 +186,12 @@
     private static String hintsToString(int hints) {
         switch (hints) {
             case 0 : return "none";
-            case NotificationListenerService.HINT_HOST_DISABLE_EFFECTS : return "disable_effects";
+            case NotificationListenerService.HINT_HOST_DISABLE_EFFECTS:
+                    return "disable_effects";
+            case NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS:
+                    return "disable_call_effects";
+            case NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS:
+                    return "disable_notification_effects";
             default: return Integer.toString(hints);
         }
     }
diff --git a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
index 4600c32..b5ddf8c 100644
--- a/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
+++ b/services/core/java/com/android/server/pm/EphemeralResolverConnection.java
@@ -69,8 +69,12 @@
     /** Intent used to bind to the service */
     private final Intent mIntent;
 
+    private static final int STATE_IDLE    = 0; // no bind operation is ongoing
+    private static final int STATE_BINDING = 1; // someone is binding and waiting
+    private static final int STATE_PENDING = 2; // a bind is pending, but the caller is not waiting
+
     @GuardedBy("mLock")
-    private volatile boolean mIsBinding;
+    private int mBindState = STATE_IDLE;
     @GuardedBy("mLock")
     private IInstantAppResolver mRemoteInstance;
 
@@ -137,23 +141,17 @@
 
     private IInstantAppResolver getRemoteInstanceLazy(String token)
             throws ConnectionException, TimeoutException, InterruptedException {
-        synchronized (mLock) {
-            if (mRemoteInstance != null) {
-                return mRemoteInstance;
-            }
-            long binderToken = Binder.clearCallingIdentity();
-            try {
-                bindLocked(token);
-            } finally {
-                Binder.restoreCallingIdentity(binderToken);
-            }
-            return mRemoteInstance;
+        long binderToken = Binder.clearCallingIdentity();
+        try {
+            return bind(token);
+        } finally {
+            Binder.restoreCallingIdentity(binderToken);
         }
     }
 
     private void waitForBindLocked(String token) throws TimeoutException, InterruptedException {
         final long startMillis = SystemClock.uptimeMillis();
-        while (mIsBinding) {
+        while (mBindState != STATE_IDLE) {
             if (mRemoteInstance != null) {
                 break;
             }
@@ -166,40 +164,81 @@
         }
     }
 
-    private void bindLocked(String token)
+    private IInstantAppResolver bind(String token)
             throws ConnectionException, TimeoutException, InterruptedException {
-        if (DEBUG_EPHEMERAL && mIsBinding && mRemoteInstance == null) {
-            Slog.i(TAG, "[" + token + "] Previous bind timed out; waiting for connection");
-        }
-        try {
-            waitForBindLocked(token);
-        } catch (TimeoutException e) {
-            if (DEBUG_EPHEMERAL) {
-                Slog.i(TAG, "[" + token + "] Previous connection never established; rebinding");
+        boolean doUnbind = false;
+        synchronized (mLock) {
+            if (mRemoteInstance != null) {
+                return mRemoteInstance;
             }
-            mContext.unbindService(mServiceConnection);
+
+            if (mBindState == STATE_PENDING) {
+                // there is a pending bind, let's see if we can use it.
+                if (DEBUG_EPHEMERAL) {
+                    Slog.i(TAG, "[" + token + "] Previous bind timed out; waiting for connection");
+                }
+                try {
+                    waitForBindLocked(token);
+                    if (mRemoteInstance != null) {
+                        return mRemoteInstance;
+                    }
+                } catch (TimeoutException e) {
+                    // nope, we might have to try a rebind.
+                    doUnbind = true;
+                }
+            }
+
+            if (mBindState == STATE_BINDING) {
+                // someone was binding when we called bind(), or they raced ahead while we were
+                // waiting in the PENDING case; wait for their result instead. Last chance!
+                if (DEBUG_EPHEMERAL) {
+                    Slog.i(TAG, "[" + token + "] Another thread is binding; waiting for connection");
+                }
+                waitForBindLocked(token);
+                // if the other thread's bindService() returned false, we could still have null.
+                if (mRemoteInstance != null) {
+                    return mRemoteInstance;
+                }
+                throw new ConnectionException(ConnectionException.FAILURE_BIND);
+            }
+            mBindState = STATE_BINDING; // our time to shine! :)
         }
-        if (mRemoteInstance != null) {
-            return;
-        }
-        mIsBinding = true;
-        if (DEBUG_EPHEMERAL) {
-            Slog.v(TAG, "[" + token + "] Binding to instant app resolver");
-        }
+
+        // only one thread can be here at a time (the one that set STATE_BINDING)
         boolean wasBound = false;
+        IInstantAppResolver instance = null;
         try {
+            if (doUnbind) {
+                if (DEBUG_EPHEMERAL) {
+                    Slog.i(TAG, "[" + token + "] Previous connection never established; rebinding");
+                }
+                mContext.unbindService(mServiceConnection);
+            }
+            if (DEBUG_EPHEMERAL) {
+                Slog.v(TAG, "[" + token + "] Binding to instant app resolver");
+            }
             final int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
             wasBound = mContext
                     .bindServiceAsUser(mIntent, mServiceConnection, flags, UserHandle.SYSTEM);
             if (wasBound) {
-                waitForBindLocked(token);
+                synchronized (mLock) {
+                    waitForBindLocked(token);
+                    instance = mRemoteInstance;
+                    return instance;
+                }
             } else {
                 Slog.w(TAG, "[" + token + "] Failed to bind to: " + mIntent);
                 throw new ConnectionException(ConnectionException.FAILURE_BIND);
             }
         } finally {
-            mIsBinding = wasBound && mRemoteInstance == null;
-            mLock.notifyAll();
+            synchronized (mLock) {
+                if (wasBound && instance == null) {
+                    mBindState = STATE_PENDING;
+                } else {
+                    mBindState = STATE_IDLE;
+                }
+                mLock.notifyAll();
+            }
         }
     }
 
@@ -255,7 +294,9 @@
             }
             synchronized (mLock) {
                 mRemoteInstance = IInstantAppResolver.Stub.asInterface(service);
-                mIsBinding = false;
+                if (mBindState == STATE_PENDING) {
+                    mBindState = STATE_IDLE;
+                }
                 try {
                     service.linkToDeath(EphemeralResolverConnection.this, 0 /*flags*/);
                 } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index bab7011..c3b93b4 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -16,17 +16,6 @@
 
 package com.android.server.pm;
 
-import static com.android.internal.util.XmlUtils.readBitmapAttribute;
-import static com.android.internal.util.XmlUtils.readBooleanAttribute;
-import static com.android.internal.util.XmlUtils.readIntAttribute;
-import static com.android.internal.util.XmlUtils.readLongAttribute;
-import static com.android.internal.util.XmlUtils.readStringAttribute;
-import static com.android.internal.util.XmlUtils.readUriAttribute;
-import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
-import static com.android.internal.util.XmlUtils.writeIntAttribute;
-import static com.android.internal.util.XmlUtils.writeLongAttribute;
-import static com.android.internal.util.XmlUtils.writeStringAttribute;
-import static com.android.internal.util.XmlUtils.writeUriAttribute;
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
@@ -54,8 +43,6 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.VersionedPackage;
 import android.graphics.Bitmap;
-import android.graphics.Bitmap.CompressFormat;
-import android.graphics.BitmapFactory;
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
@@ -84,9 +71,6 @@
 import android.util.SparseIntArray;
 import android.util.Xml;
 
-import java.io.CharArrayWriter;
-import libcore.io.IoUtils;
-
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageHelper;
@@ -97,10 +81,13 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.IoThread;
 
+import libcore.io.IoUtils;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import java.io.CharArrayWriter;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -125,32 +112,6 @@
 
     /** XML constants used in {@link #mSessionsFile} */
     private static final String TAG_SESSIONS = "sessions";
-    private static final String TAG_SESSION = "session";
-    private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
-    private static final String ATTR_SESSION_ID = "sessionId";
-    private static final String ATTR_USER_ID = "userId";
-    private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
-    private static final String ATTR_INSTALLER_UID = "installerUid";
-    private static final String ATTR_CREATED_MILLIS = "createdMillis";
-    private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
-    private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
-    private static final String ATTR_PREPARED = "prepared";
-    private static final String ATTR_SEALED = "sealed";
-    private static final String ATTR_MODE = "mode";
-    private static final String ATTR_INSTALL_FLAGS = "installFlags";
-    private static final String ATTR_INSTALL_LOCATION = "installLocation";
-    private static final String ATTR_SIZE_BYTES = "sizeBytes";
-    private static final String ATTR_APP_PACKAGE_NAME = "appPackageName";
-    @Deprecated
-    private static final String ATTR_APP_ICON = "appIcon";
-    private static final String ATTR_APP_LABEL = "appLabel";
-    private static final String ATTR_ORIGINATING_URI = "originatingUri";
-    private static final String ATTR_ORIGINATING_UID = "originatingUid";
-    private static final String ATTR_REFERRER_URI = "referrerUri";
-    private static final String ATTR_ABI_OVERRIDE = "abiOverride";
-    private static final String ATTR_VOLUME_UUID = "volumeUuid";
-    private static final String ATTR_NAME = "name";
-    private static final String ATTR_INSTALL_REASON = "installRason";
 
     /** Automatically destroy sessions older than this */
     private static final long MAX_AGE_MILLIS = 3 * DateUtils.DAY_IN_MILLIS;
@@ -357,8 +318,10 @@
             while ((type = in.next()) != END_DOCUMENT) {
                 if (type == START_TAG) {
                     final String tag = in.getName();
-                    if (TAG_SESSION.equals(tag)) {
-                        final PackageInstallerSession session = readSessionLocked(in);
+                    if (PackageInstallerSession.TAG_SESSION.equals(tag)) {
+                        final PackageInstallerSession session = PackageInstallerSession.
+                                readFromXml(in, mInternalCallback, mContext, mPm,
+                                        mInstallThread.getLooper(), mSessionsDir);
                         final long age = System.currentTimeMillis() - session.createdMillis;
 
                         final boolean valid;
@@ -397,53 +360,10 @@
         session.dump(pw);
         mHistoricalSessions.add(writer.toString());
 
+        int installerUid = session.getInstallerUid();
         // Increment the number of sessions by this installerUid.
-        mHistoricalSessionsByInstaller.put(
-                session.installerUid,
-                mHistoricalSessionsByInstaller.get(session.installerUid) + 1);
-    }
-
-    private PackageInstallerSession readSessionLocked(XmlPullParser in) throws IOException,
-            XmlPullParserException {
-        final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
-        final int userId = readIntAttribute(in, ATTR_USER_ID);
-        final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
-        final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, mPm.getPackageUid(
-                installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId));
-        final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
-        final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
-        final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
-        final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
-        final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
-        final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
-
-        final SessionParams params = new SessionParams(
-                SessionParams.MODE_INVALID);
-        params.mode = readIntAttribute(in, ATTR_MODE);
-        params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
-        params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
-        params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES);
-        params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME);
-        params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
-        params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
-        params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
-        params.originatingUid =
-                readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN);
-        params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
-        params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
-        params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
-        params.grantedRuntimePermissions = readGrantedRuntimePermissions(in);
-        params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
-
-        final File appIconFile = buildAppIconFile(sessionId);
-        if (appIconFile.exists()) {
-            params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
-            params.appIconLastModified = appIconFile.lastModified();
-        }
-
-        return new PackageInstallerSession(mInternalCallback, mContext, mPm,
-                mInstallThread.getLooper(), sessionId, userId, installerPackageName, installerUid,
-                params, createdMillis, stageDir, stageCid, prepared, sealed);
+        mHistoricalSessionsByInstaller.put(installerUid,
+                mHistoricalSessionsByInstaller.get(installerUid) + 1);
     }
 
     private void writeSessionsLocked() {
@@ -460,7 +380,7 @@
             final int size = mSessions.size();
             for (int i = 0; i < size; i++) {
                 final PackageInstallerSession session = mSessions.valueAt(i);
-                writeSessionLocked(out, session);
+                session.write(out, mSessionsDir);
             }
             out.endTag(null, TAG_SESSIONS);
             out.endDocument();
@@ -473,106 +393,6 @@
         }
     }
 
-    private void writeSessionLocked(XmlSerializer out, PackageInstallerSession session)
-            throws IOException {
-        final SessionParams params = session.params;
-
-        out.startTag(null, TAG_SESSION);
-
-        writeIntAttribute(out, ATTR_SESSION_ID, session.sessionId);
-        writeIntAttribute(out, ATTR_USER_ID, session.userId);
-        writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
-                session.installerPackageName);
-        writeIntAttribute(out, ATTR_INSTALLER_UID, session.installerUid);
-        writeLongAttribute(out, ATTR_CREATED_MILLIS, session.createdMillis);
-        if (session.stageDir != null) {
-            writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
-                    session.stageDir.getAbsolutePath());
-        }
-        if (session.stageCid != null) {
-            writeStringAttribute(out, ATTR_SESSION_STAGE_CID, session.stageCid);
-        }
-        writeBooleanAttribute(out, ATTR_PREPARED, session.isPrepared());
-        writeBooleanAttribute(out, ATTR_SEALED, session.isSealed());
-
-        writeIntAttribute(out, ATTR_MODE, params.mode);
-        writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
-        writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation);
-        writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes);
-        writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
-        writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
-        writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
-        writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid);
-        writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
-        writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
-        writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
-        writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason);
-
-        // Persist app icon if changed since last written
-        final File appIconFile = buildAppIconFile(session.sessionId);
-        if (params.appIcon == null && appIconFile.exists()) {
-            appIconFile.delete();
-        } else if (params.appIcon != null
-                && appIconFile.lastModified() != params.appIconLastModified) {
-            if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile);
-            FileOutputStream os = null;
-            try {
-                os = new FileOutputStream(appIconFile);
-                params.appIcon.compress(CompressFormat.PNG, 90, os);
-            } catch (IOException e) {
-                Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage());
-            } finally {
-                IoUtils.closeQuietly(os);
-            }
-
-            params.appIconLastModified = appIconFile.lastModified();
-        }
-
-        writeGrantedRuntimePermissions(out, params.grantedRuntimePermissions);
-
-        out.endTag(null, TAG_SESSION);
-    }
-
-    private static void writeGrantedRuntimePermissions(XmlSerializer out,
-            String[] grantedRuntimePermissions) throws IOException {
-        if (grantedRuntimePermissions != null) {
-            for (String permission : grantedRuntimePermissions) {
-                out.startTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
-                writeStringAttribute(out, ATTR_NAME, permission);
-                out.endTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
-            }
-        }
-    }
-
-    private static String[] readGrantedRuntimePermissions(XmlPullParser in)
-            throws IOException, XmlPullParserException {
-        List<String> permissions = null;
-
-        final int outerDepth = in.getDepth();
-        int type;
-        while ((type = in.next()) != XmlPullParser.END_DOCUMENT
-                && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) {
-            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
-                continue;
-            }
-            if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) {
-                String permission = readStringAttribute(in, ATTR_NAME);
-                if (permissions == null) {
-                    permissions = new ArrayList<>();
-                }
-                permissions.add(permission);
-            }
-        }
-
-        if (permissions == null) {
-            return null;
-        }
-
-        String[] permissionsArray = new String[permissions.size()];
-        permissions.toArray(permissionsArray);
-        return permissionsArray;
-    }
-
     private File buildAppIconFile(int sessionId) {
         return new File(mSessionsDir, "app_icon." + sessionId + ".png");
     }
@@ -885,9 +705,11 @@
         synchronized (mSessions) {
             for (int i = 0; i < mSessions.size(); i++) {
                 final PackageInstallerSession session = mSessions.valueAt(i);
-                if (Objects.equals(session.installerPackageName, installerPackageName)
+
+                SessionInfo info = session.generateInfo(false);
+                if (Objects.equals(info.getInstallerPackageName(), installerPackageName)
                         && session.userId == userId) {
-                    result.add(session.generateInfo(false));
+                    result.add(info);
                 }
             }
         }
@@ -962,7 +784,7 @@
         final int size = sessions.size();
         for (int i = 0; i < size; i++) {
             final PackageInstallerSession session = sessions.valueAt(i);
-            if (session.installerUid == installerUid) {
+            if (session.getInstallerUid() == installerUid) {
                 count++;
             }
         }
@@ -974,7 +796,7 @@
         if (callingUid == Process.ROOT_UID) {
             return true;
         } else {
-            return (session != null) && (callingUid == session.installerUid);
+            return (session != null) && (callingUid == session.getInstallerUid());
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 5823771..0fd696f 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -25,9 +25,22 @@
 import static android.system.OsConstants.O_RDONLY;
 import static android.system.OsConstants.O_WRONLY;
 
+import static com.android.internal.util.XmlUtils.readBitmapAttribute;
+import static com.android.internal.util.XmlUtils.readBooleanAttribute;
+import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.readLongAttribute;
+import static com.android.internal.util.XmlUtils.readStringAttribute;
+import static com.android.internal.util.XmlUtils.readUriAttribute;
+import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static com.android.internal.util.XmlUtils.writeLongAttribute;
+import static com.android.internal.util.XmlUtils.writeStringAttribute;
+import static com.android.internal.util.XmlUtils.writeUriAttribute;
 import static com.android.server.pm.PackageInstallerService.prepareExternalStageCid;
 import static com.android.server.pm.PackageInstallerService.prepareStageDir;
 
+import android.Manifest;
+import android.annotation.NonNull;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.Intent;
@@ -45,6 +58,8 @@
 import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.Signature;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.FileBridge;
@@ -53,6 +68,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
+import android.os.ParcelableException;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.RevocableFileDescriptor;
@@ -80,9 +96,14 @@
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileFilter;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.security.cert.Certificate;
 import java.util.ArrayList;
@@ -97,6 +118,34 @@
 
     private static final int MSG_COMMIT = 0;
 
+    /** XML constants used for persisting a session */
+    static final String TAG_SESSION = "session";
+    private static final String TAG_GRANTED_RUNTIME_PERMISSION = "granted-runtime-permission";
+    private static final String ATTR_SESSION_ID = "sessionId";
+    private static final String ATTR_USER_ID = "userId";
+    private static final String ATTR_INSTALLER_PACKAGE_NAME = "installerPackageName";
+    private static final String ATTR_INSTALLER_UID = "installerUid";
+    private static final String ATTR_CREATED_MILLIS = "createdMillis";
+    private static final String ATTR_SESSION_STAGE_DIR = "sessionStageDir";
+    private static final String ATTR_SESSION_STAGE_CID = "sessionStageCid";
+    private static final String ATTR_PREPARED = "prepared";
+    private static final String ATTR_SEALED = "sealed";
+    private static final String ATTR_MODE = "mode";
+    private static final String ATTR_INSTALL_FLAGS = "installFlags";
+    private static final String ATTR_INSTALL_LOCATION = "installLocation";
+    private static final String ATTR_SIZE_BYTES = "sizeBytes";
+    private static final String ATTR_APP_PACKAGE_NAME = "appPackageName";
+    @Deprecated
+    private static final String ATTR_APP_ICON = "appIcon";
+    private static final String ATTR_APP_LABEL = "appLabel";
+    private static final String ATTR_ORIGINATING_URI = "originatingUri";
+    private static final String ATTR_ORIGINATING_UID = "originatingUid";
+    private static final String ATTR_REFERRER_URI = "referrerUri";
+    private static final String ATTR_ABI_OVERRIDE = "abiOverride";
+    private static final String ATTR_VOLUME_UUID = "volumeUuid";
+    private static final String ATTR_NAME = "name";
+    private static final String ATTR_INSTALL_REASON = "installRason";
+
     // TODO: enforce INSTALL_ALLOW_TEST
     // TODO: enforce INSTALL_ALLOW_DOWNGRADE
 
@@ -104,12 +153,9 @@
     private final Context mContext;
     private final PackageManagerService mPm;
     private final Handler mHandler;
-    private final boolean mIsInstallerDeviceOwner;
 
     final int sessionId;
     final int userId;
-    final String installerPackageName;
-    final int installerUid;
     final SessionParams params;
     final long createdMillis;
     final int defaultContainerGid;
@@ -122,6 +168,17 @@
 
     private final Object mLock = new Object();
 
+    /** Uid of the creator of this session. */
+    private final int mOriginalInstallerUid;
+
+    /** Package of the owner of the installer session */
+    @GuardedBy("mLock")
+    private String mInstallerPackageName;
+
+    /** Uid of the owner of the installer session */
+    @GuardedBy("mLock")
+    private int mInstallerUid;
+
     @GuardedBy("mLock")
     private float mClientProgress = 0;
     @GuardedBy("mLock")
@@ -132,18 +189,25 @@
     @GuardedBy("mLock")
     private float mReportedProgress = -1;
 
+    /** State of the session. */
     @GuardedBy("mLock")
     private boolean mPrepared = false;
     @GuardedBy("mLock")
     private boolean mSealed = false;
     @GuardedBy("mLock")
-    private boolean mPermissionsAccepted = false;
+    private boolean mCommitted = false;
     @GuardedBy("mLock")
     private boolean mRelinquished = false;
     @GuardedBy("mLock")
     private boolean mDestroyed = false;
 
+    /** Permissions have been accepted by the user (see {@link #setPermissionsResult}) */
+    @GuardedBy("mLock")
+    private boolean mPermissionsManuallyAccepted = false;
+
+    @GuardedBy("mLock")
     private int mFinalStatus;
+    @GuardedBy("mLock")
     private String mFinalMessage;
 
     @GuardedBy("mLock")
@@ -155,9 +219,13 @@
     private IPackageInstallObserver2 mRemoteObserver;
 
     /** Fields derived from commit parsing */
+    @GuardedBy("mLock")
     private String mPackageName;
+    @GuardedBy("mLock")
     private int mVersionCode;
+    @GuardedBy("mLock")
     private Signature[] mSignatures;
+    @GuardedBy("mLock")
     private Certificate[][] mCertificates;
 
     /**
@@ -205,32 +273,61 @@
     private final Handler.Callback mHandlerCallback = new Handler.Callback() {
         @Override
         public boolean handleMessage(Message msg) {
-            // Cache package manager data without the lock held
-            final PackageInfo pkgInfo = mPm.getPackageInfo(
-                    params.appPackageName, PackageManager.GET_SIGNATURES
-                            | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
-            final ApplicationInfo appInfo = mPm.getApplicationInfo(
-                    params.appPackageName, 0, userId);
-
             synchronized (mLock) {
                 if (msg.obj != null) {
                     mRemoteObserver = (IPackageInstallObserver2) msg.obj;
                 }
-
                 try {
-                    commitLocked(pkgInfo, appInfo);
+                    commitLocked();
                 } catch (PackageManagerException e) {
                     final String completeMsg = ExceptionUtils.getCompleteMessage(e);
                     Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
                     destroyInternal();
                     dispatchSessionFinished(e.error, completeMsg, null);
                 }
-
-                return true;
             }
+
+            return true;
         }
     };
 
+    /**
+     * @return {@code true} iff the installing is app an device owner?
+     */
+    private boolean isInstallerDeviceOwnerLocked() {
+        DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+
+        return (dpm != null) && dpm.isDeviceOwnerAppOnCallingUser(
+                mInstallerPackageName);
+    }
+
+    /**
+     * Checks if the permissions still need to be confirmed.
+     *
+     * <p>This is dependant on the identity of the installer, hence this cannot be cached if the
+     * installer might still {@link #transfer(String) change}.
+     *
+     * @return {@code true} iff we need to ask to confirm the permissions?
+     */
+    private boolean needToAskForPermissionsLocked() {
+        if (mPermissionsManuallyAccepted) {
+            return false;
+        }
+
+        final boolean isPermissionGranted =
+                (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
+                        mInstallerUid) == PackageManager.PERMISSION_GRANTED);
+        final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
+        final boolean forcePermissionPrompt =
+                (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
+
+        // Device owners are allowed to silently install packages, so the permission check is
+        // waived if the installer is the device owner.
+        return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot
+                || isInstallerDeviceOwnerLocked());
+    }
+
     public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
             Context context, PackageManagerService pm, Looper looper, int sessionId, int userId,
             String installerPackageName, int installerUid, SessionParams params, long createdMillis,
@@ -242,8 +339,9 @@
 
         this.sessionId = sessionId;
         this.userId = userId;
-        this.installerPackageName = installerPackageName;
-        this.installerUid = installerUid;
+        mOriginalInstallerUid = installerUid;
+        mInstallerPackageName = installerPackageName;
+        mInstallerUid = installerUid;
         this.params = params;
         this.createdMillis = createdMillis;
         this.stageDir = stageDir;
@@ -257,26 +355,6 @@
         mPrepared = prepared;
         mSealed = sealed;
 
-        // Device owners are allowed to silently install packages, so the permission check is
-        // waived if the installer is the device owner.
-        DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(
-                Context.DEVICE_POLICY_SERVICE);
-        final boolean isPermissionGranted =
-                (mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES, installerUid)
-                        == PackageManager.PERMISSION_GRANTED);
-        final boolean isInstallerRoot = (installerUid == Process.ROOT_UID);
-        final boolean forcePermissionPrompt =
-                (params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
-        mIsInstallerDeviceOwner = (dpm != null) && dpm.isDeviceOwnerAppOnCallingUser(
-                installerPackageName);
-        if ((isPermissionGranted
-                        || isInstallerRoot
-                        || mIsInstallerDeviceOwner)
-                && !forcePermissionPrompt) {
-            mPermissionsAccepted = true;
-        } else {
-            mPermissionsAccepted = false;
-        }
         final long identity = Binder.clearCallingIdentity();
         try {
             final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
@@ -295,7 +373,7 @@
         final SessionInfo info = new SessionInfo();
         synchronized (mLock) {
             info.sessionId = sessionId;
-            info.installerPackageName = installerPackageName;
+            info.installerPackageName = mInstallerPackageName;
             info.resolvedBaseCodePath = (mResolvedBaseFile != null) ?
                     mResolvedBaseFile.getAbsolutePath() : null;
             info.progress = mProgress;
@@ -310,6 +388,13 @@
                 info.appIcon = params.appIcon;
             }
             info.appLabel = params.appLabel;
+
+            info.installLocation = params.installLocation;
+            info.originatingUri = params.originatingUri;
+            info.originatingUid = params.originatingUid;
+            info.referrerUri = params.referrerUri;
+            info.grantedRuntimePermissions = params.grantedRuntimePermissions;
+            info.installFlags = params.installFlags;
         }
         return info;
     }
@@ -326,14 +411,26 @@
         }
     }
 
-    private void assertPreparedAndNotSealed(String cookie) {
-        synchronized (mLock) {
-            if (!mPrepared) {
-                throw new IllegalStateException(cookie + " before prepared");
-            }
-            if (mSealed) {
-                throw new SecurityException(cookie + " not allowed after commit");
-            }
+    private void assertPreparedAndNotSealedLocked(String cookie) {
+        assertPreparedAndNotCommittedOrDestroyedLocked(cookie);
+        if (mSealed) {
+            throw new SecurityException(cookie + " not allowed after sealing");
+        }
+    }
+
+    private void assertPreparedAndNotCommittedOrDestroyedLocked(String cookie) {
+        assertPreparedAndNotDestroyedLocked(cookie);
+        if (mCommitted) {
+            throw new SecurityException(cookie + " not allowed after commit");
+        }
+    }
+
+    private void assertPreparedAndNotDestroyedLocked(String cookie) {
+        if (!mPrepared) {
+            throw new IllegalStateException(cookie + " before prepared");
+        }
+        if (mDestroyed) {
+            throw new SecurityException(cookie + " not allowed after destruction");
         }
     }
 
@@ -342,27 +439,27 @@
      * might point at an ASEC mount point, which is why we delay path resolution
      * until someone actively works with the session.
      */
-    private File resolveStageDir() throws IOException {
-        synchronized (mLock) {
-            if (mResolvedStageDir == null) {
-                if (stageDir != null) {
-                    mResolvedStageDir = stageDir;
+    private File resolveStageDirLocked() throws IOException {
+        if (mResolvedStageDir == null) {
+            if (stageDir != null) {
+                mResolvedStageDir = stageDir;
+            } else {
+                final String path = PackageHelper.getSdDir(stageCid);
+                if (path != null) {
+                    mResolvedStageDir = new File(path);
                 } else {
-                    final String path = PackageHelper.getSdDir(stageCid);
-                    if (path != null) {
-                        mResolvedStageDir = new File(path);
-                    } else {
-                        throw new IOException("Failed to resolve path to container " + stageCid);
-                    }
+                    throw new IOException("Failed to resolve path to container " + stageCid);
                 }
             }
-            return mResolvedStageDir;
         }
+        return mResolvedStageDir;
     }
 
     @Override
     public void setClientProgress(float progress) {
         synchronized (mLock) {
+            assertCallerIsOwnerOrRootLocked();
+
             // Always publish first staging movement
             final boolean forcePublish = (mClientProgress == 0);
             mClientProgress = progress;
@@ -373,6 +470,8 @@
     @Override
     public void addClientProgress(float progress) {
         synchronized (mLock) {
+            assertCallerIsOwnerOrRootLocked();
+
             setClientProgress(mClientProgress + progress);
         }
     }
@@ -390,11 +489,15 @@
 
     @Override
     public String[] getNames() {
-        assertPreparedAndNotSealed("getNames");
-        try {
-            return resolveStageDir().list();
-        } catch (IOException e) {
-            throw ExceptionUtils.wrap(e);
+        synchronized (mLock) {
+            assertCallerIsOwnerOrRootLocked();
+            assertPreparedAndNotCommittedOrDestroyedLocked("getNames");
+
+            try {
+                return resolveStageDirLocked().list();
+            } catch (IOException e) {
+                throw ExceptionUtils.wrap(e);
+            }
         }
     }
 
@@ -403,20 +506,26 @@
         if (TextUtils.isEmpty(params.appPackageName)) {
             throw new IllegalStateException("Must specify package name to remove a split");
         }
-        try {
-            createRemoveSplitMarker(splitName);
-        } catch (IOException e) {
-            throw ExceptionUtils.wrap(e);
+
+        synchronized (mLock) {
+            assertCallerIsOwnerOrRootLocked();
+            assertPreparedAndNotCommittedOrDestroyedLocked("removeSplit");
+
+            try {
+                createRemoveSplitMarkerLocked(splitName);
+            } catch (IOException e) {
+                throw ExceptionUtils.wrap(e);
+            }
         }
     }
 
-    private void createRemoveSplitMarker(String splitName) throws IOException {
+    private void createRemoveSplitMarkerLocked(String splitName) throws IOException {
         try {
             final String markerName = splitName + REMOVE_SPLIT_MARKER_EXTENSION;
             if (!FileUtils.isValidExtFilename(markerName)) {
                 throw new IllegalArgumentException("Invalid marker: " + markerName);
             }
-            final File target = new File(resolveStageDir(), markerName);
+            final File target = new File(resolveStageDirLocked(), markerName);
             target.createNewFile();
             Os.chmod(target.getAbsolutePath(), 0 /*mode*/);
         } catch (ErrnoException e) {
@@ -440,8 +549,10 @@
         // will block any attempted install transitions.
         final RevocableFileDescriptor fd;
         final FileBridge bridge;
+        final File stageDir;
         synchronized (mLock) {
-            assertPreparedAndNotSealed("openWrite");
+            assertCallerIsOwnerOrRootLocked();
+            assertPreparedAndNotSealedLocked("openWrite");
 
             if (PackageInstaller.ENABLE_REVOCABLE_FD) {
                 fd = new RevocableFileDescriptor();
@@ -452,6 +563,8 @@
                 bridge = new FileBridge();
                 mBridges.add(bridge);
             }
+
+            stageDir = resolveStageDirLocked();
         }
 
         try {
@@ -462,7 +575,7 @@
             final File target;
             final long identity = Binder.clearCallingIdentity();
             try {
-                target = new File(resolveStageDir(), name);
+                target = new File(stageDir, name);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -500,55 +613,108 @@
 
     @Override
     public ParcelFileDescriptor openRead(String name) {
-        try {
-            return openReadInternal(name);
-        } catch (IOException e) {
-            throw ExceptionUtils.wrap(e);
+        synchronized (mLock) {
+            assertCallerIsOwnerOrRootLocked();
+            assertPreparedAndNotCommittedOrDestroyedLocked("openRead");
+            try {
+                return openReadInternalLocked(name);
+            } catch (IOException e) {
+                throw ExceptionUtils.wrap(e);
+            }
         }
     }
 
-    private ParcelFileDescriptor openReadInternal(String name) throws IOException {
-        assertPreparedAndNotSealed("openRead");
-
+    private ParcelFileDescriptor openReadInternalLocked(String name) throws IOException {
         try {
             if (!FileUtils.isValidExtFilename(name)) {
                 throw new IllegalArgumentException("Invalid name: " + name);
             }
-            final File target = new File(resolveStageDir(), name);
-
+            final File target = new File(resolveStageDirLocked(), name);
             final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(), O_RDONLY, 0);
             return new ParcelFileDescriptor(targetFd);
-
         } catch (ErrnoException e) {
             throw e.rethrowAsIOException();
         }
     }
 
+    /**
+     * Check if the caller is the owner of this session. Otherwise throw a
+     * {@link SecurityException}.
+     */
+    private void assertCallerIsOwnerOrRootLocked() {
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid) {
+            throw new SecurityException("Session does not belong to uid " + callingUid);
+        }
+    }
+
+    /**
+     * If anybody is reading or writing data of the session, throw an {@link SecurityException}.
+     */
+    private void assertNoWriteFileTransfersOpenLocked() {
+        // Verify that all writers are hands-off
+        for (RevocableFileDescriptor fd : mFds) {
+            if (!fd.isRevoked()) {
+                throw new SecurityException("Files still open");
+            }
+        }
+        for (FileBridge bridge : mBridges) {
+            if (!bridge.isClosed()) {
+                throw new SecurityException("Files still open");
+            }
+        }
+    }
+
     @Override
-    public void commit(IntentSender statusReceiver) {
+    public void commit(IntentSender statusReceiver, boolean forTransfer) {
         Preconditions.checkNotNull(statusReceiver);
 
+        // Cache package manager data without the lock held
+        final PackageInfo installedPkgInfo = mPm.getPackageInfo(
+                params.appPackageName, PackageManager.GET_SIGNATURES
+                        | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
+
         final boolean wasSealed;
         synchronized (mLock) {
+            assertCallerIsOwnerOrRootLocked();
+            assertPreparedAndNotDestroyedLocked("commit");
+
+            if (forTransfer) {
+                mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null);
+
+                if (mInstallerUid == mOriginalInstallerUid) {
+                    throw new IllegalArgumentException("Session has not been transferred");
+                }
+            } else {
+                if (mInstallerUid != mOriginalInstallerUid) {
+                    throw new IllegalArgumentException("Session has been transferred");
+                }
+            }
+
             wasSealed = mSealed;
             if (!mSealed) {
-                // Verify that all writers are hands-off
-                for (RevocableFileDescriptor fd : mFds) {
-                    if (!fd.isRevoked()) {
-                        throw new SecurityException("Files still open");
-                    }
+                try {
+                    sealAndValidateLocked(installedPkgInfo);
+                } catch (PackageManagerException e) {
+                    // Do now throw an exception here to stay compatible with O and older
+                    destroyInternal();
+                    dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null);
+                    return;
                 }
-                for (FileBridge bridge : mBridges) {
-                    if (!bridge.isClosed()) {
-                        throw new SecurityException("Files still open");
-                    }
-                }
-                mSealed = true;
             }
 
             // Client staging is fully done at this point
             mClientProgress = 1f;
             computeProgressLocked(true);
+
+            // This ongoing commit should keep session active, even though client
+            // will probably close their end.
+            mActiveCount.incrementAndGet();
+
+            mCommitted = true;
+            final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(
+                    mContext, statusReceiver, sessionId, isInstallerDeviceOwnerLocked(), userId);
+            mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
         }
 
         if (!wasSealed) {
@@ -557,17 +723,82 @@
             // the session lock, since otherwise it's a lock inversion.
             mCallback.onSessionSealedBlocking(this);
         }
-
-        // This ongoing commit should keep session active, even though client
-        // will probably close their end.
-        mActiveCount.incrementAndGet();
-
-        final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
-                statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
-        mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
     }
 
-    private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
+    /**
+     * Seal the session to prevent further modification and validate the contents of it.
+     *
+     * <p>The session will be sealed after calling this method even if it failed.
+     *
+     * @param pkgInfo The package info for {@link #params}.packagename
+     */
+    private void sealAndValidateLocked(PackageInfo pkgInfo)
+            throws PackageManagerException {
+        assertNoWriteFileTransfersOpenLocked();
+
+        mSealed = true;
+
+        // Verify that stage looks sane with respect to existing application.
+        // This currently only ensures packageName, versionCode, and certificate
+        // consistency.
+        validateInstallLocked(pkgInfo);
+
+        // Read transfers from the original owner stay open, but as the session's data
+        // cannot be modified anymore, there is no leak of information.
+    }
+
+    @Override
+    public void transfer(String packageName) {
+        Preconditions.checkNotNull(packageName);
+
+        ApplicationInfo newOwnerAppInfo = mPm.getApplicationInfo(packageName, 0, userId);
+        if (newOwnerAppInfo == null) {
+            throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
+        }
+
+        if (PackageManager.PERMISSION_GRANTED != mPm.checkUidPermission(
+                Manifest.permission.INSTALL_PACKAGES, newOwnerAppInfo.uid)) {
+            throw new SecurityException("Destination package " + packageName + " does not have "
+                    + "the " + Manifest.permission.INSTALL_PACKAGES + " permission");
+        }
+
+        // Cache package manager data without the lock held
+        final PackageInfo installedPkgInfo = mPm.getPackageInfo(
+                params.appPackageName, PackageManager.GET_SIGNATURES
+                        | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId);
+
+        // Only install flags that can be verified by the app the session is transferred to are
+        // allowed. The parameters can be read via PackageInstaller.SessionInfo.
+        if (!params.areHiddenOptionsSet()) {
+            throw new SecurityException("Can only transfer sessions that use public options");
+        }
+
+        synchronized (mLock) {
+            assertCallerIsOwnerOrRootLocked();
+            assertPreparedAndNotSealedLocked("transfer");
+
+            try {
+                sealAndValidateLocked(installedPkgInfo);
+            } catch (PackageManagerException e) {
+                throw new IllegalArgumentException("Package is not valid", e);
+            }
+
+            if (!mPackageName.equals(mInstallerPackageName)) {
+                throw new SecurityException("Can only transfer sessions that update the original "
+                        + "installer");
+            }
+
+            mInstallerPackageName = packageName;
+            mInstallerUid = newOwnerAppInfo.uid;
+        }
+
+        // Persist the fact that we've sealed ourselves to prevent
+        // mutations of any hard links we create. We do this without holding
+        // the session lock, since otherwise it's a lock inversion.
+        mCallback.onSessionSealedBlocking(this);
+    }
+
+    private void commitLocked()
             throws PackageManagerException {
         if (mDestroyed) {
             throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
@@ -577,22 +808,17 @@
         }
 
         try {
-            resolveStageDir();
+            resolveStageDirLocked();
         } catch (IOException e) {
             throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
                     "Failed to resolve stage location", e);
         }
 
-        // Verify that stage looks sane with respect to existing application.
-        // This currently only ensures packageName, versionCode, and certificate
-        // consistency.
-        validateInstallLocked(pkgInfo, appInfo);
-
         Preconditions.checkNotNull(mPackageName);
         Preconditions.checkNotNull(mSignatures);
         Preconditions.checkNotNull(mResolvedBaseFile);
 
-        if (!mPermissionsAccepted) {
+        if (needToAskForPermissionsLocked()) {
             // User needs to accept permissions; give installer an intent they
             // can use to involve user.
             final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS);
@@ -622,7 +848,7 @@
         if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
             try {
                 final List<File> fromFiles = mResolvedInheritedFiles;
-                final File toDir = resolveStageDir();
+                final File toDir = resolveStageDirLocked();
 
                 if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
                 if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
@@ -683,7 +909,7 @@
 
         mRelinquished = true;
         mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
-                installerPackageName, installerUid, user, mCertificates);
+                mInstallerPackageName, mInstallerUid, user, mCertificates);
     }
 
     /**
@@ -698,8 +924,7 @@
      * Note that upgrade compatibility is still performed by
      * {@link PackageManagerService}.
      */
-    private void validateInstallLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
-            throws PackageManagerException {
+    private void validateInstallLocked(PackageInfo pkgInfo) throws PackageManagerException {
         mPackageName = null;
         mVersionCode = -1;
         mSignatures = null;
@@ -752,7 +977,7 @@
                 mCertificates = apk.certificates;
             }
 
-            assertApkConsistent(String.valueOf(addedFile), apk);
+            assertApkConsistentLocked(String.valueOf(addedFile), apk);
 
             // Take this opportunity to enforce uniform naming
             final String targetName;
@@ -807,13 +1032,14 @@
 
         } else {
             // Partial installs must be consistent with existing install
-            if (appInfo == null) {
+            if (pkgInfo == null || pkgInfo.applicationInfo == null) {
                 throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
                         "Missing existing base package for " + mPackageName);
             }
 
             final PackageLite existing;
             final ApkLite existingBase;
+            ApplicationInfo appInfo = pkgInfo.applicationInfo;
             try {
                 existing = PackageParser.parsePackageLite(new File(appInfo.getCodePath()), 0);
                 existingBase = PackageParser.parseApkLite(new File(appInfo.getBaseCodePath()),
@@ -822,7 +1048,7 @@
                 throw PackageManagerException.from(e);
             }
 
-            assertApkConsistent("Existing base", existingBase);
+            assertApkConsistentLocked("Existing base", existingBase);
 
             // Inherit base if not overridden
             if (mResolvedBaseFile == null) {
@@ -878,7 +1104,7 @@
         }
     }
 
-    private void assertApkConsistent(String tag, ApkLite apk)
+    private void assertApkConsistentLocked(String tag, ApkLite apk)
             throws PackageManagerException {
         if (!mPackageName.equals(apk.packageName)) {
             throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
@@ -959,6 +1185,15 @@
         return true;
     }
 
+    /**
+     * @return the uid of the owner this session
+     */
+    public int getInstallerUid() {
+        synchronized (mLock) {
+            return mInstallerUid;
+        }
+    }
+
     private static String getRelativePath(File file, File base) throws IOException {
         final String pathStr = file.getAbsolutePath();
         final String baseStr = base.getAbsolutePath();
@@ -1106,9 +1341,9 @@
         if (accepted) {
             // Mark and kick off another install pass
             synchronized (mLock) {
-                mPermissionsAccepted = true;
+                mPermissionsManuallyAccepted = true;
+                mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
             }
-            mHandler.obtainMessage(MSG_COMMIT).sendToTarget();
         } else {
             destroyInternal();
             dispatchSessionFinished(INSTALL_FAILED_ABORTED, "User rejected permissions", null);
@@ -1120,7 +1355,9 @@
             mCallback.onSessionActiveChanged(this, true);
         }
 
+        boolean wasPrepared;
         synchronized (mLock) {
+            wasPrepared = mPrepared;
             if (!mPrepared) {
                 if (stageDir != null) {
                     prepareStageDir(stageDir);
@@ -1141,13 +1378,20 @@
                 }
 
                 mPrepared = true;
-                mCallback.onSessionPrepared(this);
             }
         }
+
+        if (!wasPrepared) {
+            mCallback.onSessionPrepared(this);
+        }
     }
 
     @Override
     public void close() {
+        synchronized (mLock) {
+            assertCallerIsOwnerOrRootLocked();
+        }
+
         if (mActiveCount.decrementAndGet() == 0) {
             mCallback.onSessionActiveChanged(this, false);
         }
@@ -1155,21 +1399,33 @@
 
     @Override
     public void abandon() {
-        if (mRelinquished) {
-            Slog.d(TAG, "Ignoring abandon after commit relinquished control");
-            return;
+        synchronized (mLock) {
+            assertCallerIsOwnerOrRootLocked();
+
+            if (mRelinquished) {
+                Slog.d(TAG, "Ignoring abandon after commit relinquished control");
+                return;
+            }
+            destroyInternal();
         }
-        destroyInternal();
+
         dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
     }
 
     private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
-        mFinalStatus = returnCode;
-        mFinalMessage = msg;
+        IPackageInstallObserver2 observer;
+        String packageName;
+        synchronized (mLock) {
+            mFinalStatus = returnCode;
+            mFinalMessage = msg;
 
-        if (mRemoteObserver != null) {
+            observer = mRemoteObserver;
+            packageName = mPackageName;
+        }
+
+        if (observer != null) {
             try {
-                mRemoteObserver.onPackageInstalled(mPackageName, returnCode, msg, extras);
+                observer.onPackageInstalled(packageName, returnCode, msg, extras);
             } catch (RemoteException ignored) {
             }
         }
@@ -1220,8 +1476,9 @@
         pw.increaseIndent();
 
         pw.printPair("userId", userId);
-        pw.printPair("installerPackageName", installerPackageName);
-        pw.printPair("installerUid", installerUid);
+        pw.printPair("mOriginalInstallerUid", mOriginalInstallerUid);
+        pw.printPair("mInstallerPackageName", mInstallerPackageName);
+        pw.printPair("mInstallerUid", mInstallerUid);
         pw.printPair("createdMillis", createdMillis);
         pw.printPair("stageDir", stageDir);
         pw.printPair("stageCid", stageCid);
@@ -1232,7 +1489,7 @@
         pw.printPair("mClientProgress", mClientProgress);
         pw.printPair("mProgress", mProgress);
         pw.printPair("mSealed", mSealed);
-        pw.printPair("mPermissionsAccepted", mPermissionsAccepted);
+        pw.printPair("mPermissionsManuallyAccepted", mPermissionsManuallyAccepted);
         pw.printPair("mRelinquished", mRelinquished);
         pw.printPair("mDestroyed", mDestroyed);
         pw.printPair("mFds", mFds.size());
@@ -1243,4 +1500,170 @@
 
         pw.decreaseIndent();
     }
+
+    private static void writeGrantedRuntimePermissionsLocked(XmlSerializer out,
+            String[] grantedRuntimePermissions) throws IOException {
+        if (grantedRuntimePermissions != null) {
+            for (String permission : grantedRuntimePermissions) {
+                out.startTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
+                writeStringAttribute(out, ATTR_NAME, permission);
+                out.endTag(null, TAG_GRANTED_RUNTIME_PERMISSION);
+            }
+        }
+    }
+
+    private static File buildAppIconFile(int sessionId, @NonNull File sessionsDir) {
+        return new File(sessionsDir, "app_icon." + sessionId + ".png");
+    }
+
+    /**
+     * Write this session to a {@link XmlSerializer}.
+     *
+     * @param out Where to write the session to
+     * @param sessionsDir The directory containing the sessions
+     */
+    void write(@NonNull XmlSerializer out, @NonNull File sessionsDir) throws IOException {
+        synchronized (mLock) {
+            out.startTag(null, TAG_SESSION);
+
+            writeIntAttribute(out, ATTR_SESSION_ID, sessionId);
+            writeIntAttribute(out, ATTR_USER_ID, userId);
+            writeStringAttribute(out, ATTR_INSTALLER_PACKAGE_NAME,
+                    mInstallerPackageName);
+            writeIntAttribute(out, ATTR_INSTALLER_UID, mInstallerUid);
+            writeLongAttribute(out, ATTR_CREATED_MILLIS, createdMillis);
+            if (stageDir != null) {
+                writeStringAttribute(out, ATTR_SESSION_STAGE_DIR,
+                        stageDir.getAbsolutePath());
+            }
+            if (stageCid != null) {
+                writeStringAttribute(out, ATTR_SESSION_STAGE_CID, stageCid);
+            }
+            writeBooleanAttribute(out, ATTR_PREPARED, isPrepared());
+            writeBooleanAttribute(out, ATTR_SEALED, isSealed());
+
+            writeIntAttribute(out, ATTR_MODE, params.mode);
+            writeIntAttribute(out, ATTR_INSTALL_FLAGS, params.installFlags);
+            writeIntAttribute(out, ATTR_INSTALL_LOCATION, params.installLocation);
+            writeLongAttribute(out, ATTR_SIZE_BYTES, params.sizeBytes);
+            writeStringAttribute(out, ATTR_APP_PACKAGE_NAME, params.appPackageName);
+            writeStringAttribute(out, ATTR_APP_LABEL, params.appLabel);
+            writeUriAttribute(out, ATTR_ORIGINATING_URI, params.originatingUri);
+            writeIntAttribute(out, ATTR_ORIGINATING_UID, params.originatingUid);
+            writeUriAttribute(out, ATTR_REFERRER_URI, params.referrerUri);
+            writeStringAttribute(out, ATTR_ABI_OVERRIDE, params.abiOverride);
+            writeStringAttribute(out, ATTR_VOLUME_UUID, params.volumeUuid);
+            writeIntAttribute(out, ATTR_INSTALL_REASON, params.installReason);
+
+            // Persist app icon if changed since last written
+            File appIconFile = buildAppIconFile(sessionId, sessionsDir);
+            if (params.appIcon == null && appIconFile.exists()) {
+                appIconFile.delete();
+            } else if (params.appIcon != null
+                    && appIconFile.lastModified() != params.appIconLastModified) {
+                if (LOGD) Slog.w(TAG, "Writing changed icon " + appIconFile);
+                FileOutputStream os = null;
+                try {
+                    os = new FileOutputStream(appIconFile);
+                    params.appIcon.compress(Bitmap.CompressFormat.PNG, 90, os);
+                } catch (IOException e) {
+                    Slog.w(TAG, "Failed to write icon " + appIconFile + ": " + e.getMessage());
+                } finally {
+                    IoUtils.closeQuietly(os);
+                }
+
+                params.appIconLastModified = appIconFile.lastModified();
+            }
+
+            writeGrantedRuntimePermissionsLocked(out, params.grantedRuntimePermissions);
+        }
+
+        out.endTag(null, TAG_SESSION);
+    }
+
+    private static String[] readGrantedRuntimePermissions(XmlPullParser in)
+            throws IOException, XmlPullParserException {
+        List<String> permissions = null;
+
+        final int outerDepth = in.getDepth();
+        int type;
+        while ((type = in.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || in.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+            if (TAG_GRANTED_RUNTIME_PERMISSION.equals(in.getName())) {
+                String permission = readStringAttribute(in, ATTR_NAME);
+                if (permissions == null) {
+                    permissions = new ArrayList<>();
+                }
+                permissions.add(permission);
+            }
+        }
+
+        if (permissions == null) {
+            return null;
+        }
+
+        String[] permissionsArray = new String[permissions.size()];
+        permissions.toArray(permissionsArray);
+        return permissionsArray;
+    }
+
+    /**
+     * Read new session from a {@link XmlPullParser xml description} and create it.
+     *
+     * @param in The source of the description
+     * @param callback Callback the session uses to notify about changes of it's state
+     * @param context Context to be used by the session
+     * @param pm PackageManager to use by the session
+     * @param installerThread Thread to be used for callbacks of this session
+     * @param sessionsDir The directory the sessions are stored in
+     *
+     * @return The newly created session
+     */
+    public static PackageInstallerSession readFromXml(@NonNull XmlPullParser in,
+            @NonNull PackageInstallerService.InternalCallback callback, @NonNull Context context,
+            @NonNull PackageManagerService pm, Looper installerThread, @NonNull File sessionsDir)
+            throws IOException, XmlPullParserException {
+        final int sessionId = readIntAttribute(in, ATTR_SESSION_ID);
+        final int userId = readIntAttribute(in, ATTR_USER_ID);
+        final String installerPackageName = readStringAttribute(in, ATTR_INSTALLER_PACKAGE_NAME);
+        final int installerUid = readIntAttribute(in, ATTR_INSTALLER_UID, pm.getPackageUid(
+                installerPackageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId));
+        final long createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS);
+        final String stageDirRaw = readStringAttribute(in, ATTR_SESSION_STAGE_DIR);
+        final File stageDir = (stageDirRaw != null) ? new File(stageDirRaw) : null;
+        final String stageCid = readStringAttribute(in, ATTR_SESSION_STAGE_CID);
+        final boolean prepared = readBooleanAttribute(in, ATTR_PREPARED, true);
+        final boolean sealed = readBooleanAttribute(in, ATTR_SEALED);
+
+        final SessionParams params = new SessionParams(
+                SessionParams.MODE_INVALID);
+        params.mode = readIntAttribute(in, ATTR_MODE);
+        params.installFlags = readIntAttribute(in, ATTR_INSTALL_FLAGS);
+        params.installLocation = readIntAttribute(in, ATTR_INSTALL_LOCATION);
+        params.sizeBytes = readLongAttribute(in, ATTR_SIZE_BYTES);
+        params.appPackageName = readStringAttribute(in, ATTR_APP_PACKAGE_NAME);
+        params.appIcon = readBitmapAttribute(in, ATTR_APP_ICON);
+        params.appLabel = readStringAttribute(in, ATTR_APP_LABEL);
+        params.originatingUri = readUriAttribute(in, ATTR_ORIGINATING_URI);
+        params.originatingUid =
+                readIntAttribute(in, ATTR_ORIGINATING_UID, SessionParams.UID_UNKNOWN);
+        params.referrerUri = readUriAttribute(in, ATTR_REFERRER_URI);
+        params.abiOverride = readStringAttribute(in, ATTR_ABI_OVERRIDE);
+        params.volumeUuid = readStringAttribute(in, ATTR_VOLUME_UUID);
+        params.grantedRuntimePermissions = readGrantedRuntimePermissions(in);
+        params.installReason = readIntAttribute(in, ATTR_INSTALL_REASON);
+
+        final File appIconFile = buildAppIconFile(sessionId, sessionsDir);
+        if (appIconFile.exists()) {
+            params.appIcon = BitmapFactory.decodeFile(appIconFile.getAbsolutePath());
+            params.appIconLastModified = appIconFile.lastModified();
+        }
+
+        return new PackageInstallerSession(callback, context, pm,
+                installerThread, sessionId, userId, installerPackageName, installerUid,
+                params, createdMillis, stageDir, stageCid, prepared, sealed);
+    }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 61e39bf..ff45c10 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -143,6 +143,7 @@
 import android.content.pm.IPackageInstallObserver2;
 import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageManager;
+import android.content.pm.IPackageManagerNative;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
 import android.content.pm.InstantAppInfo;
@@ -2304,6 +2305,8 @@
                 factoryTest, onlyCore);
         m.enableSystemUserPackages();
         ServiceManager.addService("package", m);
+        final PackageManagerNative pmn = m.new PackageManagerNative();
+        ServiceManager.addService("package_native", pmn);
         return m;
     }
 
@@ -2722,14 +2725,17 @@
             //delete tmp files
             deleteTempPackageFiles();
 
+            final int cachedSystemApps = PackageParser.sCachedPackageReadCount.get();
+
             // Remove any shared userIDs that have no associated packages
             mSettings.pruneSharedUsersLPw();
             final long systemScanTime = SystemClock.uptimeMillis() - startTime;
             final int systemPackagesCount = mPackages.size();
             Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime
                     + " ms, packageCount: " + systemPackagesCount
-                    + " ms, timePerPackage: "
-                    + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount));
+                    + " , timePerPackage: "
+                    + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
+                    + " , cached: " + cachedSystemApps);
             if (mIsUpgrade && systemPackagesCount > 0) {
                 MetricsLogger.histogram(null, "ota_package_manager_system_app_avg_scan_time",
                         ((int) systemScanTime) / systemPackagesCount);
@@ -2820,12 +2826,16 @@
                 // This must be done last to ensure all stubs are replaced or disabled.
                 decompressSystemApplications(stubSystemApps, scanFlags);
 
+                final int cachedNonSystemApps = PackageParser.sCachedPackageReadCount.get()
+                                - cachedSystemApps;
+
                 final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
                 final int dataPackagesCount = mPackages.size() - systemPackagesCount;
                 Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
                         + " ms, packageCount: " + dataPackagesCount
-                        + " ms, timePerPackage: "
-                        + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount));
+                        + " , timePerPackage: "
+                        + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
+                        + " , cached: " + cachedNonSystemApps);
                 if (mIsUpgrade && dataPackagesCount > 0) {
                     MetricsLogger.histogram(null, "ota_package_manager_data_app_avg_scan_time",
                             ((int) dataScanTime) / dataPackagesCount);
@@ -6413,8 +6423,40 @@
                 }
                 return ps.name;
             }
+            return null;
         }
-        return null;
+    }
+
+    @Override
+    public String[] getNamesForUids(int[] uids) {
+        if (uids == null || uids.length == 0) {
+            return null;
+        }
+        final int callingUid = Binder.getCallingUid();
+        if (getInstantAppPackageName(callingUid) != null) {
+            return null;
+        }
+        final String[] names = new String[uids.length];
+        synchronized (mPackages) {
+            for (int i = uids.length - 1; i >= 0; i--) {
+                final int uid = uids[i];
+                Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
+                if (obj instanceof SharedUserSetting) {
+                    final SharedUserSetting sus = (SharedUserSetting) obj;
+                    names[i] = "shared:" + sus.name;
+                } else if (obj instanceof PackageSetting) {
+                    final PackageSetting ps = (PackageSetting) obj;
+                    if (filterAppAccessLPr(ps, callingUid, UserHandle.getUserId(callingUid))) {
+                        names[i] = null;
+                    } else {
+                        names[i] = ps.name;
+                    }
+                } else {
+                    names[i] = null;
+                }
+            }
+        }
+        return names;
     }
 
     @Override
@@ -9981,6 +10023,7 @@
     public void shutdown() {
         mPackageUsage.writeNow(mPackages);
         mCompilerStats.writeNow();
+        mDexManager.savePackageDexUsageNow();
     }
 
     @Override
@@ -24773,6 +24816,20 @@
         }
     }
 
+    private class PackageManagerNative extends IPackageManagerNative.Stub {
+        @Override
+        public String[] getNamesForUids(int[] uids) throws RemoteException {
+            final String[] results = PackageManagerService.this.getNamesForUids(uids);
+            // massage results so they can be parsed by the native binder
+            for (int i = results.length - 1; i >= 0; --i) {
+                if (results[i] == null) {
+                    results[i] = "";
+                }
+            }
+            return results;
+        }
+    }
+
     private class PackageManagerInternalImpl extends PackageManagerInternal {
         @Override
         public void setLocationPackagesProvider(PackagesProvider provider) {
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 79e02b5..947e01c4 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -592,6 +592,13 @@
         return existingValue == null ? newValue : existingValue;
     }
 
+    /**
+     * Saves the in-memory package dex usage to disk right away.
+     */
+    public void savePackageDexUsageNow() {
+        mPackageDexUsage.writeNow();
+    }
+
     public static class RegisterDexModuleResult {
         public RegisterDexModuleResult() {
             this(false, null);
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index 8819aa6..6ee26d3 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -198,8 +198,12 @@
      * Convenience method for async writes which does not force the user to pass a useless
      * (Void) null.
      */
-    public void maybeWriteAsync() {
-      maybeWriteAsync((Void) null);
+    /*package*/ void maybeWriteAsync() {
+      maybeWriteAsync(null);
+    }
+
+    /*package*/ void writeNow() {
+        writeInternal(null);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e3cf459..ae78d7c 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -496,6 +496,7 @@
     volatile boolean mEndCallKeyHandled;
     volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
     volatile boolean mGoingToSleep;
+    volatile boolean mRequestedOrGoingToSleep;
     volatile boolean mRecentsVisible;
     volatile boolean mPictureInPictureVisible;
     // Written by vr manager thread, only read in this class.
@@ -1274,7 +1275,7 @@
         if (gestureService != null) {
             gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(event, interactive,
                     mTmpBoolean);
-            if (mTmpBoolean.value && mGoingToSleep) {
+            if (mTmpBoolean.value && mRequestedOrGoingToSleep) {
                 mCameraGestureTriggeredDuringGoingToSleep = true;
             }
         }
@@ -1402,17 +1403,14 @@
                 case SHORT_PRESS_POWER_NOTHING:
                     break;
                 case SHORT_PRESS_POWER_GO_TO_SLEEP:
-                    mPowerManager.goToSleep(eventTime,
-                            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
+                    goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                     break;
                 case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
-                    mPowerManager.goToSleep(eventTime,
-                            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
+                    goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                     break;
                 case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
-                    mPowerManager.goToSleep(eventTime,
-                            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
+                    goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
                             PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                     launchHomeFromHotKey();
                     break;
@@ -1437,6 +1435,11 @@
         }
     }
 
+    private void goToSleep(long eventTime, int reason, int flags) {
+        mRequestedOrGoingToSleep = true;
+        mPowerManager.goToSleep(eventTime, reason, flags);
+    }
+
     private void shortPressPowerGoHome() {
         launchHomeFromHotKey(true /* awakenFromDreams */, false /*respectKeyguard*/);
         if (isKeyguardShowingAndNotOccluded()) {
@@ -1469,8 +1472,7 @@
                             Settings.Global.THEATER_MODE_ON, 1);
 
                     if (mGoToSleepOnButtonPressTheaterMode && interactive) {
-                        mPowerManager.goToSleep(eventTime,
-                                PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
+                        goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                     }
                 }
                 break;
@@ -1553,8 +1555,7 @@
             case SHORT_PRESS_SLEEP_GO_TO_SLEEP:
             case SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME:
                 Slog.i(TAG, "sleepRelease() calling goToSleep(GO_TO_SLEEP_REASON_SLEEP_BUTTON)");
-                mPowerManager.goToSleep(eventTime,
-                       PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
+                goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
                 break;
         }
     }
@@ -4184,7 +4185,7 @@
         }
 
         @Override
-        public void onInputEvent(InputEvent event) {
+        public void onInputEvent(InputEvent event, int displayId) {
             boolean handled = false;
             try {
                 if (event instanceof MotionEvent
@@ -6060,7 +6061,7 @@
                             }
                             if ((mEndcallBehavior
                                     & Settings.System.END_BUTTON_BEHAVIOR_SLEEP) != 0) {
-                                mPowerManager.goToSleep(event.getEventTime(),
+                                goToSleep(event.getEventTime(),
                                         PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                                 isWakeKey = false;
                             }
@@ -6569,8 +6570,10 @@
     @Override
     public void startedGoingToSleep(int why) {
         if (DEBUG_WAKEUP) Slog.i(TAG, "Started going to sleep... (why=" + why + ")");
-        mCameraGestureTriggeredDuringGoingToSleep = false;
+
         mGoingToSleep = true;
+        mRequestedOrGoingToSleep = true;
+
         if (mKeyguardDelegate != null) {
             mKeyguardDelegate.onStartedGoingToSleep(why);
         }
@@ -6584,6 +6587,7 @@
         MetricsLogger.histogram(mContext, "screen_timeout", mLockScreenTimeout / 1000);
 
         mGoingToSleep = false;
+        mRequestedOrGoingToSleep = false;
 
         // We must get this work done here because the power manager will drop
         // the wake lock and let the system suspend once this function returns.
@@ -7049,6 +7053,12 @@
                 // Ignore sensor when demo rotation lock is enabled.
                 // Note that the dock orientation and HDMI rotation lock override this.
                 preferredRotation = mDemoRotation;
+            } else if (mPersistentVrModeEnabled) {
+                // While in VR, apps always prefer a portrait rotation. This does not change
+                // any apps that explicitly set landscape, but does cause sensors be ignored,
+                // and ignored any orientation lock that the user has set (this conditional
+                // should remain above the ORIENTATION_LOCKED conditional below).
+                preferredRotation = mPortraitRotation;
             } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LOCKED) {
                 // Application just wants to remain locked in the last rotation.
                 preferredRotation = lastRotation;
@@ -7079,13 +7089,7 @@
                         || mAllowAllRotations == 1
                         || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
                         || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_USER) {
-                    // In VrMode, we report the sensor as always being in default orientation so:
-                    // 1) The orientation doesn't change as the user moves their head.
-                    // 2) 2D apps within VR show in the device's default orientation.
-                    // This only overwrites the sensor-provided orientation and does not affect any
-                    // explicit orientation preferences specified by any activities.
-                    preferredRotation =
-                            mPersistentVrModeEnabled ? Surface.ROTATION_0 : sensorRotation;
+                    preferredRotation = sensorRotation;
                 } else {
                     preferredRotation = lastRotation;
                 }
@@ -7498,8 +7502,7 @@
 
     private void applyLidSwitchState() {
         if (mLidState == LID_CLOSED && mLidControlsSleep) {
-            mPowerManager.goToSleep(SystemClock.uptimeMillis(),
-                    PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
+            goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH,
                     PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
         } else if (mLidState == LID_CLOSED && mLidControlsScreenLock) {
             mWindowManagerFuncs.lockDeviceNow();
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index e894275..92f1c83 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -57,6 +57,7 @@
 import android.widget.TextView;
 
 import com.android.internal.telephony.ITelephony;
+import com.android.server.RescueParty;
 import com.android.server.pm.PackageManagerService;
 
 import java.io.File;
@@ -293,11 +294,20 @@
                             com.android.internal.R.string.reboot_to_update_reboot));
             }
         } else if (mReason != null && mReason.equals(PowerManager.REBOOT_RECOVERY)) {
-            // Factory reset path. Set the dialog message accordingly.
-            pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
-            pd.setMessage(context.getText(
-                        com.android.internal.R.string.reboot_to_reset_message));
-            pd.setIndeterminate(true);
+            if (RescueParty.isAttemptingFactoryReset()) {
+                // We're not actually doing a factory reset yet; we're rebooting
+                // to ask the user if they'd like to reset, so give them a less
+                // scary dialog message.
+                pd.setTitle(context.getText(com.android.internal.R.string.power_off));
+                pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
+                pd.setIndeterminate(true);
+            } else {
+                // Factory reset path. Set the dialog message accordingly.
+                pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
+                pd.setMessage(context.getText(
+                            com.android.internal.R.string.reboot_to_reset_message));
+                pd.setIndeterminate(true);
+            }
         } else if (mReason != null && mReason.equals(PowerManager.SHUTDOWN_USER_REQUESTED)) {
             Dialog d = new Dialog(context);
             d.setContentView(com.android.internal.R.layout.shutdown_dialog);
diff --git a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
index 88b6d87..a35383f 100644
--- a/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
+++ b/services/core/java/com/android/server/storage/DeviceStorageMonitorService.java
@@ -29,6 +29,7 @@
 import android.os.Environment;
 import android.os.FileObserver;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Message;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -154,20 +155,8 @@
 
     private static final String TV_NOTIFICATION_CHANNEL_ID = "devicestoragemonitor.tv";
 
-    /**
-     * Handler that checks the amount of disk space on the device and sends a
-     * notification if the device runs low on disk space
-     */
-    private final Handler mHandler = new Handler(IoThread.get().getLooper()) {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_CHECK:
-                    check();
-                    return;
-            }
-        }
-    };
+    private final HandlerThread mHandlerThread;
+    private final Handler mHandler;
 
     private State findOrCreateState(UUID uuid) {
         State state = mStates.get(uuid);
@@ -256,6 +245,20 @@
 
     public DeviceStorageMonitorService(Context context) {
         super(context);
+
+        mHandlerThread = new HandlerThread(TAG, android.os.Process.THREAD_PRIORITY_BACKGROUND);
+        mHandlerThread.start();
+
+        mHandler = new Handler(mHandlerThread.getLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                switch (msg.what) {
+                    case MSG_CHECK:
+                        check();
+                        return;
+                }
+            }
+        };
     }
 
     private static boolean isBootImageOnDisk() {
diff --git a/services/core/java/com/android/server/vr/Vr2dDisplay.java b/services/core/java/com/android/server/vr/Vr2dDisplay.java
index 69d8ca6..8f50a39 100644
--- a/services/core/java/com/android/server/vr/Vr2dDisplay.java
+++ b/services/core/java/com/android/server/vr/Vr2dDisplay.java
@@ -24,6 +24,7 @@
 import android.service.vr.IVrManager;
 import android.util.Log;
 import android.view.Surface;
+import android.view.WindowManagerInternal;
 
 import com.android.server.vr.VrManagerService;
 
@@ -74,6 +75,7 @@
     public static final int MIN_VR_DISPLAY_DPI = 1;
 
     private final ActivityManagerInternal mActivityManagerInternal;
+    private final WindowManagerInternal mWindowManagerInternal;
     private final DisplayManager mDisplayManager;
     private final IVrManager mVrManager;
     private final Object mVdLock = new Object();
@@ -103,9 +105,11 @@
     private boolean mBootsToVr = false;  // The device boots into VR (standalone VR device)
 
     public Vr2dDisplay(DisplayManager displayManager,
-           ActivityManagerInternal activityManagerInternal, IVrManager vrManager) {
+           ActivityManagerInternal activityManagerInternal,
+           WindowManagerInternal windowManagerInternal, IVrManager vrManager) {
         mDisplayManager = displayManager;
         mActivityManagerInternal = activityManagerInternal;
+        mWindowManagerInternal = windowManagerInternal;
         mVrManager = vrManager;
         mVirtualDisplayWidth = DEFAULT_VIRTUAL_DISPLAY_WIDTH;
         mVirtualDisplayHeight = DEFAULT_VIRTUAL_DISPLAY_HEIGHT;
@@ -296,13 +300,12 @@
                     UNIQUE_DISPLAY_ID);
 
             if (mVirtualDisplay != null) {
-                mActivityManagerInternal.setVr2dDisplayId(
-                    mVirtualDisplay.getDisplay().getDisplayId());
+                updateDisplayId(mVirtualDisplay.getDisplay().getDisplayId());
                 // Now create the ImageReader to supply a Surface to the new virtual display.
                 startImageReader();
             } else {
                 Log.w(TAG, "Virtual display id is null after createVirtualDisplay");
-                mActivityManagerInternal.setVr2dDisplayId(INVALID_DISPLAY);
+                updateDisplayId(INVALID_DISPLAY);
                 return;
             }
         }
@@ -310,6 +313,11 @@
         Log.i(TAG, "VD created: " + mVirtualDisplay);
     }
 
+    private void updateDisplayId(int displayId) {
+        mActivityManagerInternal.setVr2dDisplayId(displayId);
+        mWindowManagerInternal.setVr2dDisplayId(displayId);
+    }
+
     /**
      * Stops the virtual display with a {@link #STOP_VIRTUAL_DISPLAY_DELAY_MILLIS} timeout.
      * The timeout prevents the virtual display from bouncing in cases where VrMode goes in and out
@@ -325,7 +333,7 @@
                     } else {
                         Log.i(TAG, "Stopping Virtual Display");
                         synchronized (mVdLock) {
-                            mActivityManagerInternal.setVr2dDisplayId(INVALID_DISPLAY);
+                            updateDisplayId(INVALID_DISPLAY);
                             setSurfaceLocked(null); // clean up and release the surface first.
                             if (mVirtualDisplay != null) {
                                 mVirtualDisplay.release();
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 425b23f..b6b964b 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -58,6 +58,7 @@
 import android.util.ArraySet;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.WindowManagerInternal;
 
 import com.android.internal.R;
 import com.android.internal.util.DumpUtils;
@@ -633,8 +634,11 @@
 
             DisplayManager dm =
                     (DisplayManager) getContext().getSystemService(Context.DISPLAY_SERVICE);
-            ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
-            mVr2dDisplay = new Vr2dDisplay(dm, ami, mVrManager);
+            mVr2dDisplay = new Vr2dDisplay(
+                    dm,
+                    LocalServices.getService(ActivityManagerInternal.class),
+                    LocalServices.getService(WindowManagerInternal.class),
+                    mVrManager);
             mVr2dDisplay.init(getContext(), mBootsToVr);
 
             IntentFilter intentFilter = new IntentFilter();
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 802054e..46097ed 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -25,6 +25,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
@@ -63,6 +64,7 @@
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IInterface;
 import android.os.IRemoteCallback;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
@@ -331,11 +333,18 @@
         }
     }
 
-    private void notifyWallpaperColorsChanged(WallpaperData wallpaper, int which) {
+    private void notifyWallpaperColorsChanged(@NonNull WallpaperData wallpaper, int which) {
         boolean needsExtraction;
         synchronized (mLock) {
-            if (mColorsChangedListeners.getRegisteredCallbackCount() == 0)
+            final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners =
+                    mColorsChangedListeners.get(wallpaper.userId);
+            final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners =
+                    mColorsChangedListeners.get(UserHandle.USER_ALL);
+            // No-op until someone is listening to it.
+            if (emptyCallbackList(currentUserColorListeners)  &&
+                    emptyCallbackList(userAllColorListeners)) {
                 return;
+            }
 
             if (DEBUG) {
                 Slog.v(TAG, "notifyWallpaperColorsChanged " + which);
@@ -346,40 +355,66 @@
 
         // Let's notify the current values, it's fine if it's null, it just means
         // that we don't know yet.
-        notifyColorListeners(wallpaper.primaryColors, which);
+        notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId);
 
         if (needsExtraction) {
             extractColors(wallpaper);
-            notifyColorListeners(wallpaper.primaryColors, which);
+            synchronized (mLock) {
+                // Don't need to notify if nothing changed.
+                if (wallpaper.primaryColors == null) {
+                    return;
+                }
+            }
+            notifyColorListeners(wallpaper.primaryColors, which, wallpaper.userId);
         }
     }
 
-    private void notifyColorListeners(WallpaperColors wallpaperColors, int which) {
-        final IWallpaperManagerCallback[] listeners;
+    private static <T extends IInterface> boolean emptyCallbackList(RemoteCallbackList<T> list) {
+        return (list == null || list.getRegisteredCallbackCount() == 0);
+    }
+
+    private void notifyColorListeners(@NonNull WallpaperColors wallpaperColors, int which,
+            int userId) {
         final IWallpaperManagerCallback keyguardListener;
+        final RemoteCallbackList<IWallpaperManagerCallback> currentUserColorListeners;
+        final RemoteCallbackList<IWallpaperManagerCallback> userAllColorListeners;
         synchronized (mLock) {
-            // Make a synchronized copy of the listeners to avoid concurrent list modification.
-            int callbackCount = mColorsChangedListeners.beginBroadcast();
-            listeners = new IWallpaperManagerCallback[callbackCount];
-            for (int i = 0; i < callbackCount; i++) {
-                listeners[i] = mColorsChangedListeners.getBroadcastItem(i);
-            }
-            mColorsChangedListeners.finishBroadcast();
+            currentUserColorListeners = mColorsChangedListeners.get(userId);
+            userAllColorListeners = mColorsChangedListeners.get(UserHandle.USER_ALL);
             keyguardListener = mKeyguardListener;
         }
 
-        for (int i = 0; i < listeners.length; i++) {
-            try {
-                listeners[i].onWallpaperColorsChanged(wallpaperColors, which);
-            } catch (RemoteException e) {
-                // Callback is gone, it's not necessary to unregister it since
-                // RemoteCallbackList#getBroadcastItem will take care of it.
+        if (currentUserColorListeners != null) {
+            int count = currentUserColorListeners.beginBroadcast();
+            for (int i = 0; i < count; i++) {
+                try {
+                    currentUserColorListeners.getBroadcastItem(i)
+                            .onWallpaperColorsChanged(wallpaperColors, which, userId);
+                } catch (RemoteException e) {
+                    // Callback is gone, it's not necessary to unregister it since
+                    // RemoteCallbackList#getBroadcastItem will take care of it.
+                }
             }
+            currentUserColorListeners.finishBroadcast();
+        }
+
+        if (userAllColorListeners != null) {
+            int count = userAllColorListeners.beginBroadcast();
+            for (int i = 0; i < count; i++) {
+                try {
+                    userAllColorListeners.getBroadcastItem(i)
+                            .onWallpaperColorsChanged(wallpaperColors, which, userId);
+                } catch (RemoteException e) {
+                    // Callback is gone, it's not necessary to unregister it since
+                    // RemoteCallbackList#getBroadcastItem will take care of it.
+                }
+            }
+            userAllColorListeners.finishBroadcast();
         }
 
         if (keyguardListener != null) {
             try {
-                keyguardListener.onWallpaperColorsChanged(wallpaperColors, which);
+                keyguardListener.onWallpaperColorsChanged(wallpaperColors, which, userId);
             } catch (RemoteException e) {
                 // Oh well it went away; no big deal
             }
@@ -595,7 +630,11 @@
     final IPackageManager mIPackageManager;
     final MyPackageMonitor mMonitor;
     final AppOpsManager mAppOpsManager;
-    final RemoteCallbackList<IWallpaperManagerCallback> mColorsChangedListeners;
+    /**
+     * Map of color listeners per user id.
+     * The key will be the id of a user or UserHandle.USER_ALL - for wildcard listeners.
+     */
+    final SparseArray<RemoteCallbackList<IWallpaperManagerCallback>> mColorsChangedListeners;
     WallpaperData mLastWallpaper;
     IWallpaperManagerCallback mKeyguardListener;
     boolean mWaitingForUnlock;
@@ -858,11 +897,6 @@
          */
         @Override
         public void onWallpaperColorsChanged(WallpaperColors primaryColors) {
-            // Do not override default color extraction if API isn't implemented.
-            if (primaryColors == null) {
-                return;
-            }
-
             int which;
             synchronized (mLock) {
                 // Do not broadcast changes on ImageWallpaper since it's handled
@@ -876,7 +910,7 @@
                 // Live wallpapers always are system wallpapers.
                 which = FLAG_SYSTEM;
                 // It's also the lock screen wallpaper when we don't have a bitmap in there
-                WallpaperData lockedWallpaper = mLockWallpaperMap.get(mCurrentUserId);
+                WallpaperData lockedWallpaper = mLockWallpaperMap.get(mWallpaper.userId);
                 if (lockedWallpaper == null) {
                     which |= FLAG_LOCK;
                 }
@@ -906,6 +940,13 @@
                     }
                     mPaddingChanged = false;
                 }
+                try {
+                    // This will trigger onComputeColors in the wallpaper engine.
+                    // It's fine to be locked in here since the binder is oneway.
+                    mEngine.requestWallpaperColors();
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Failed to request wallpaper colors", e);
+                }
             }
         }
 
@@ -1096,7 +1137,7 @@
         mMonitor.register(context, null, UserHandle.ALL, true);
         getWallpaperDir(UserHandle.USER_SYSTEM).mkdirs();
         loadSettingsLocked(UserHandle.USER_SYSTEM, false);
-        mColorsChangedListeners = new RemoteCallbackList<>();
+        mColorsChangedListeners = new SparseArray<>();
     }
 
     private static File getWallpaperDir(int userId) {
@@ -1252,16 +1293,24 @@
     }
 
     void switchUser(int userId, IRemoteCallback reply) {
+        WallpaperData systemWallpaper;
+        WallpaperData lockWallpaper;
         synchronized (mLock) {
             mCurrentUserId = userId;
-            WallpaperData wallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
-            // Not started watching yet, in case wallpaper data was loaded for other reasons.
-            if (wallpaper.wallpaperObserver == null) {
-                wallpaper.wallpaperObserver = new WallpaperObserver(wallpaper);
-                wallpaper.wallpaperObserver.startWatching();
+            systemWallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
+            lockWallpaper = mLockWallpaperMap.get(userId);
+            if (lockWallpaper == null) {
+                lockWallpaper = systemWallpaper;
             }
-            switchWallpaper(wallpaper, reply);
+            // Not started watching yet, in case wallpaper data was loaded for other reasons.
+            if (systemWallpaper.wallpaperObserver == null) {
+                systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper);
+                systemWallpaper.wallpaperObserver.startWatching();
+            }
+            switchWallpaper(systemWallpaper, reply);
         }
+        notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM);
+        notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
     }
 
     void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
@@ -1634,16 +1683,30 @@
     }
 
     @Override
-    public void registerWallpaperColorsCallback(IWallpaperManagerCallback cb) {
+    public void registerWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId) {
+        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                userId, true, true, "registerWallpaperColorsCallback", null);
         synchronized (mLock) {
-            mColorsChangedListeners.register(cb);
+            RemoteCallbackList<IWallpaperManagerCallback> userColorsChangedListeners =
+                    mColorsChangedListeners.get(userId);
+            if (userColorsChangedListeners == null) {
+                userColorsChangedListeners = new RemoteCallbackList<>();
+                mColorsChangedListeners.put(userId, userColorsChangedListeners);
+            }
+            userColorsChangedListeners.register(cb);
         }
     }
 
     @Override
-    public void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb) {
+    public void unregisterWallpaperColorsCallback(IWallpaperManagerCallback cb, int userId) {
+        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                userId, true, true, "unregisterWallpaperColorsCallback", null);
         synchronized (mLock) {
-            mColorsChangedListeners.unregister(cb);
+            final RemoteCallbackList<IWallpaperManagerCallback> userColorsChangedListeners =
+                    mColorsChangedListeners.get(userId);
+            if (userColorsChangedListeners != null) {
+                userColorsChangedListeners.unregister(cb);
+            }
         }
     }
 
@@ -1657,23 +1720,25 @@
     }
 
     @Override
-    public WallpaperColors getWallpaperColors(int which) throws RemoteException {
+    public WallpaperColors getWallpaperColors(int which, int userId) throws RemoteException {
         if (which != FLAG_LOCK && which != FLAG_SYSTEM) {
             throw new IllegalArgumentException("which should be either FLAG_LOCK or FLAG_SYSTEM");
         }
+        userId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+                userId, false, true, "getWallpaperColors", null);
 
         WallpaperData wallpaperData = null;
         boolean shouldExtract;
 
         synchronized (mLock) {
             if (which == FLAG_LOCK) {
-                wallpaperData = mLockWallpaperMap.get(mCurrentUserId);
+                wallpaperData = mLockWallpaperMap.get(userId);
             }
 
             // Try to get the system wallpaper anyway since it might
             // also be the lock screen wallpaper
             if (wallpaperData == null) {
-                wallpaperData = mWallpaperMap.get(mCurrentUserId);
+                wallpaperData = mWallpaperMap.get(userId);
             }
 
             if (wallpaperData == null) {
@@ -1872,8 +1937,11 @@
 
             try {
                 wallpaper.imageWallpaperPending = false;
+                boolean same = changingToSame(name, wallpaper);
                 if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) {
-                    wallpaper.primaryColors = null;
+                    if (!same) {
+                        wallpaper.primaryColors = null;
+                    }
                     wallpaper.wallpaperId = makeWallpaperIdLocked();
                     notifyCallbacksLocked(wallpaper);
                     shouldNotifyColors = true;
@@ -1888,26 +1956,31 @@
         }
     }
 
+    private boolean changingToSame(ComponentName componentName, WallpaperData wallpaper) {
+        if (wallpaper.connection != null) {
+            if (wallpaper.wallpaperComponent == null) {
+                if (componentName == null) {
+                    if (DEBUG) Slog.v(TAG, "changingToSame: still using default");
+                    // Still using default wallpaper.
+                    return true;
+                }
+            } else if (wallpaper.wallpaperComponent.equals(componentName)) {
+                // Changing to same wallpaper.
+                if (DEBUG) Slog.v(TAG, "same wallpaper");
+                return true;
+            }
+        }
+        return false;
+    }
+
     boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force,
             boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
         if (DEBUG_LIVE) {
             Slog.v(TAG, "bindWallpaperComponentLocked: componentName=" + componentName);
         }
         // Has the component changed?
-        if (!force) {
-            if (wallpaper.connection != null) {
-                if (wallpaper.wallpaperComponent == null) {
-                    if (componentName == null) {
-                        if (DEBUG) Slog.v(TAG, "bindWallpaperComponentLocked: still using default");
-                        // Still using default wallpaper.
-                        return true;
-                    }
-                } else if (wallpaper.wallpaperComponent.equals(componentName)) {
-                    // Changing to same wallpaper.
-                    if (DEBUG) Slog.v(TAG, "same wallpaper");
-                    return true;
-                }
-            }
+        if (!force && changingToSame(componentName, wallpaper)) {
+            return true;
         }
 
         try {
@@ -2234,15 +2307,35 @@
     }
 
     private void migrateFromOld() {
-        File oldWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
-        File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
-        if (oldWallpaper.exists()) {
-            File newWallpaper = new File(getWallpaperDir(0), WALLPAPER);
-            oldWallpaper.renameTo(newWallpaper);
-        }
-        if (oldInfo.exists()) {
-            File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO);
-            oldInfo.renameTo(newInfo);
+        // Pre-N, what existed is the one we're now using as the display crop
+        File preNWallpaper = new File(getWallpaperDir(0), WALLPAPER_CROP);
+        // In the very-long-ago, imagery lived with the settings app
+        File originalWallpaper = new File(WallpaperBackupHelper.WALLPAPER_IMAGE_KEY);
+        File newWallpaper = new File(getWallpaperDir(0), WALLPAPER);
+
+        // Migrations from earlier wallpaper image storage schemas
+        if (preNWallpaper.exists()) {
+            if (!newWallpaper.exists()) {
+                // we've got the 'wallpaper' crop file but not the nominal source image,
+                // so do the simple "just take everything" straight copy of legacy data
+                if (DEBUG) {
+                    Slog.i(TAG, "Migrating wallpaper schema");
+                }
+                FileUtils.copyFile(preNWallpaper, newWallpaper);
+            } // else we're in the usual modern case: both source & crop exist
+        } else if (originalWallpaper.exists()) {
+            // VERY old schema; make sure things exist and are in the right place
+            if (DEBUG) {
+                Slog.i(TAG, "Migrating antique wallpaper schema");
+            }
+            File oldInfo = new File(WallpaperBackupHelper.WALLPAPER_INFO_KEY);
+            if (oldInfo.exists()) {
+                File newInfo = new File(getWallpaperDir(0), WALLPAPER_INFO);
+                oldInfo.renameTo(newInfo);
+            }
+
+            FileUtils.copyFile(originalWallpaper, preNWallpaper);
+            originalWallpaper.renameTo(newWallpaper);
         }
     }
 
@@ -2303,12 +2396,12 @@
         JournaledFile journal = makeJournaledFile(userId);
         FileInputStream stream = null;
         File file = journal.chooseForRead();
-        if (!file.exists()) {
-            // This should only happen one time, when upgrading from a legacy system
-            migrateFromOld();
-        }
+
         WallpaperData wallpaper = mWallpaperMap.get(userId);
         if (wallpaper == null) {
+            // Do this once per boot
+            migrateFromOld();
+
             wallpaper = new WallpaperData(userId, WALLPAPER, WALLPAPER_CROP);
             wallpaper.allowBackup = true;
             mWallpaperMap.put(userId, wallpaper);
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 78f2195..805250a 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -17,7 +17,7 @@
 package com.android.server.wm;
 
 import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MAGNIFICATION_REGION_EFFECT;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -539,7 +539,7 @@
                     WindowState windowState = visibleWindows.valueAt(i);
                     if ((windowState.mAttrs.type == TYPE_MAGNIFICATION_OVERLAY)
                             || ((windowState.mAttrs.privateFlags
-                            & PRIVATE_FLAG_NO_MAGNIFICATION_REGION_EFFECT) != 0)) {
+                            & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0)) {
                         continue;
                     }
 
diff --git a/services/core/java/com/android/server/wm/AppWindowAnimator.java b/services/core/java/com/android/server/wm/AppWindowAnimator.java
index f3a09ed..ddbbde1 100644
--- a/services/core/java/com/android/server/wm/AppWindowAnimator.java
+++ b/services/core/java/com/android/server/wm/AppWindowAnimator.java
@@ -198,6 +198,14 @@
         return animation != null || mAppToken.inPendingTransaction;
     }
 
+    /**
+     * @return whether an animation is about to start, i.e. the animation is set already but we
+     *         haven't processed the first frame yet.
+     */
+    boolean isAnimationStarting() {
+        return animation != null && !animating;
+    }
+
     public int getTransit() {
         return mTransit;
     }
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 84fafe2..e9696d2 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -487,9 +487,8 @@
             }
 
             final WindowState mainWin = mContainer.findMainWindow();
-            if (mainWin != null && mainWin.isVisible() && mainWin.isDrawnLw()) {
-                // App already has a visible window that is drawn...why would you want a starting
-                // window?
+            if (mainWin != null && mainWin.mWinAnimator.getShown()) {
+                // App already has a visible window...why would you want a starting window?
                 return false;
             }
 
@@ -612,23 +611,7 @@
         return mContainer.getTask().getConfiguration().orientation == snapshot.getOrientation();
     }
 
-    /**
-     * Remove starting window if the app is currently hidden. It is possible the starting window is
-     * part of its app exit transition animation in which case we delay hiding the app token. The
-     * method allows for removal when window manager has set the app token to hidden.
-     */
-    public void removeHiddenStartingWindow() {
-        synchronized (mWindowMap) {
-            if (!mContainer.hidden) {
-                if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Starting window app still visible."
-                        + " Ignoring remove request.");
-                return;
-            }
-            removeStartingWindow();
-        }
-    }
-
-    void removeStartingWindow() {
+    public void removeStartingWindow() {
         synchronized (mWindowMap) {
             if (mContainer.startingWindow == null) {
                 if (mContainer.startingData != null) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index c4ff455..2e4de8c 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -193,6 +193,11 @@
 
     Task mLastParent;
 
+    /**
+     * See {@link #canTurnScreenOn()}
+     */
+    private boolean mCanTurnScreenOn = true;
+
     AppWindowToken(WindowManagerService service, IApplicationToken token, boolean voiceInteraction,
             DisplayContent dc, long inputDispatchingTimeoutNanos, boolean fullscreen,
             boolean showForAllUsers, int targetSdk, int orientation, int rotationAnimationHint,
@@ -290,7 +295,7 @@
         boolean nowGone = mReportedVisibilityResults.nowGone;
 
         boolean nowDrawn = numInteresting > 0 && numDrawn >= numInteresting;
-        boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting;
+        boolean nowVisible = numInteresting > 0 && numVisible >= numInteresting && !hidden;
         if (!nowGone) {
             // If the app is not yet gone, then it can only become visible/drawn.
             if (!nowDrawn) {
@@ -448,7 +453,6 @@
                     mChildren.get(i).mWinAnimator.hide("immediately hidden");
                 }
                 SurfaceControl.closeTransaction();
-                removeStartingWindow();
             }
 
             if (!mService.mClosingApps.contains(this) && !mService.mOpeningApps.contains(this)) {
@@ -526,12 +530,6 @@
         return super.checkCompleteDeferredRemoval();
     }
 
-    private void removeStartingWindow() {
-        if (startingData != null && getController() != null) {
-            getController().removeStartingWindow();
-        }
-    }
-
     void onRemovedFromDisplay() {
         if (mRemovingFromDisplay) {
             return;
@@ -559,7 +557,9 @@
         if (DEBUG_ADD_REMOVE || DEBUG_TOKEN_MOVEMENT) Slog.v(TAG_WM, "removeAppToken: "
                 + this + " delayed=" + delayed + " Callers=" + Debug.getCallers(4));
 
-        removeStartingWindow();
+        if (startingData != null && getController() != null) {
+            getController().removeStartingWindow();
+        }
 
         // If this window was animating, then we need to ensure that the app transition notifies
         // that animations have completed in WMS.handleAnimatingStoppedAndTransitionLocked(), so
@@ -649,6 +649,8 @@
         if (DEBUG_ADD_REMOVE) Slog.v(TAG, "notifyAppResumed: wasStopped=" + wasStopped
                 + " " + this);
         mAppStopped = false;
+        // Allow the window to turn the screen on once the app is resumed again.
+        setCanTurnScreenOn(true);
         if (!wasStopped) {
             destroySurfaces(true /*cleanupOnResume*/);
         }
@@ -1646,6 +1648,24 @@
     }
 
     /**
+     * Sets whether the current launch can turn the screen on. See {@link #canTurnScreenOn()}
+     */
+    void setCanTurnScreenOn(boolean canTurnScreenOn) {
+        mCanTurnScreenOn = canTurnScreenOn;
+    }
+
+    /**
+     * Indicates whether the current launch can turn the screen on. This is to prevent multiple
+     * relayouts from turning the screen back on. The screen should only turn on at most
+     * once per activity resume.
+     *
+     * @return true if the screen can be turned on.
+     */
+    boolean canTurnScreenOn() {
+        return mCanTurnScreenOn;
+    }
+
+    /**
      * Retrieves whether we'd like to generate a snapshot that's based solely on the theme. This is
      * the case when preview screenshots are disabled {@link #setDisablePreviewScreenshots} or when
      * we can't take a snapshot for other reasons, for example, if we have a secure window.
diff --git a/services/core/java/com/android/server/wm/PointerEventDispatcher.java b/services/core/java/com/android/server/wm/PointerEventDispatcher.java
index 6b0e4c9..484987e 100644
--- a/services/core/java/com/android/server/wm/PointerEventDispatcher.java
+++ b/services/core/java/com/android/server/wm/PointerEventDispatcher.java
@@ -36,11 +36,11 @@
     }
 
     @Override
-    public void onInputEvent(InputEvent event) {
+    public void onInputEvent(InputEvent event, int displayId) {
         try {
             if (event instanceof MotionEvent
                     && (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
-                final MotionEvent motionEvent = (MotionEvent)event;
+                final MotionEvent motionEvent = (MotionEvent) event;
                 PointerEventListener[] listeners;
                 synchronized (mListeners) {
                     if (mListenersArray == null) {
@@ -50,7 +50,7 @@
                     listeners = mListenersArray;
                 }
                 for (int i = 0; i < listeners.length; ++i) {
-                    listeners[i].onPointerEvent(motionEvent);
+                    listeners[i].onPointerEvent(motionEvent, displayId);
                 }
             }
         } finally {
diff --git a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
index 0508fdf..a12c2c4 100644
--- a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
+++ b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
@@ -32,7 +32,7 @@
 // the surface control.
 //
 // See cts/hostsidetests/../../SurfaceTraceReceiver.java for parsing side.
-class RemoteSurfaceTrace extends SurfaceControl {
+class RemoteSurfaceTrace extends SurfaceControlWithBackground {
     static final String TAG = "RemoteSurfaceTrace";
 
     final FileDescriptor mWriteFd;
@@ -41,7 +41,8 @@
     final WindowManagerService mService;
     final WindowState mWindow;
 
-    RemoteSurfaceTrace(FileDescriptor fd, SurfaceControl wrapped, WindowState window) {
+    RemoteSurfaceTrace(FileDescriptor fd, SurfaceControlWithBackground wrapped,
+            WindowState window) {
         super(wrapped);
 
         mWriteFd = fd;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 233e75b..40528d0 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -18,6 +18,7 @@
 
 import android.content.res.Configuration;
 import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
 import android.hardware.power.V1_0.PowerHint;
 import android.os.Binder;
 import android.os.Debug;
@@ -243,12 +244,24 @@
                     displayId, displayInfo);
             mService.configureDisplayPolicyLocked(dc);
 
-            // TODO(multi-display): Create an input channel for each display with touch capability.
-            if (displayId == DEFAULT_DISPLAY && mService.canDispatchPointerEvents()) {
-                dc.mTapDetector = new TaskTapPointerEventListener(
-                        mService, dc);
+            // Tap Listeners are supported for:
+            // 1. All physical displays (multi-display).
+            // 2. VirtualDisplays that support virtual touch input. (Only VR for now)
+            // TODO(multi-display): Support VirtualDisplays with no virtual touch input.
+            if ((display.getType() != Display.TYPE_VIRTUAL
+                    || (display.getType() == Display.TYPE_VIRTUAL
+                        // Only VR VirtualDisplays
+                        && displayId == mService.mVr2dDisplayId))
+                    && mService.canDispatchPointerEvents()) {
+                if (DEBUG_DISPLAY) {
+                    Slog.d(TAG,
+                            "Registering PointerEventListener for DisplayId: " + displayId);
+                }
+                dc.mTapDetector = new TaskTapPointerEventListener(mService, dc);
                 mService.registerPointerEventListener(dc.mTapDetector);
-                mService.registerPointerEventListener(mService.mMousePositionTracker);
+                if (displayId == DEFAULT_DISPLAY) {
+                    mService.registerPointerEventListener(mService.mMousePositionTracker);
+                }
             }
         }
 
@@ -747,12 +760,16 @@
 
         if (mUpdateRotation) {
             if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation");
-            // TODO(multi-display): Update rotation for different displays separately.
-            final int displayId = defaultDisplay.getDisplayId();
-            if (defaultDisplay.updateRotationUnchecked(false /* inTransaction */)) {
-                mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
-            } else {
-                mUpdateRotation = false;
+
+            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                final DisplayContent displayContent = mChildren.get(displayNdx);
+                final int displayId = displayContent.getDisplayId();
+                if (displayContent.updateRotationUnchecked(false /* inTransaction */)) {
+                    mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
+                } else if (displayId == DEFAULT_DISPLAY) {
+                    // TODO(multi-display): Track rotation updates for different displays separately
+                    mUpdateRotation = false;
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java b/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java
new file mode 100644
index 0000000..f5ef2e6
--- /dev/null
+++ b/services/core/java/com/android/server/wm/SurfaceControlWithBackground.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2017 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.wm;
+
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.view.Surface;
+import android.view.Surface.OutOfResourcesException;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+/**
+ * SurfaceControl extension that has background sized to match its container.
+ */
+class SurfaceControlWithBackground extends SurfaceControl {
+    // SurfaceControl that holds the background behind opaque letterboxed app windows.
+    private SurfaceControl mBackgroundControl;
+
+    // Flags that define whether the background should be shown.
+    private boolean mOpaque;
+    private boolean mVisible;
+
+    // Way to communicate with corresponding window.
+    private WindowSurfaceController mWindowSurfaceController;
+
+    // Rect to hold task bounds when computing metrics for background.
+    private Rect mTmpContainerRect = new Rect();
+
+    // Last metrics applied to the main SurfaceControl.
+    private float mLastWidth, mLastHeight;
+    private float mLastDsDx = 1, mLastDsDy = 1;
+    private float mLastX, mLastY;
+
+    public SurfaceControlWithBackground(SurfaceControlWithBackground other) {
+        super(other);
+        mBackgroundControl = other.mBackgroundControl;
+        mOpaque = other.mOpaque;
+        mVisible = other.mVisible;
+        mWindowSurfaceController = other.mWindowSurfaceController;
+    }
+
+    public SurfaceControlWithBackground(SurfaceSession s, String name, int w, int h, int format,
+            int flags, int windowType, int ownerUid,
+            WindowSurfaceController windowSurfaceController) throws OutOfResourcesException {
+        super(s, name, w, h, format, flags, windowType, ownerUid);
+
+        // We should only show background when the window is letterboxed in a task.
+        if (!windowSurfaceController.mAnimator.mWin.isLetterboxedAppWindow()) {
+            return;
+        }
+        mWindowSurfaceController = windowSurfaceController;
+        mLastWidth = w;
+        mLastHeight = h;
+        mOpaque = (flags & SurfaceControl.OPAQUE) != 0;
+        mWindowSurfaceController.getContainerRect(mTmpContainerRect);
+        mBackgroundControl = new SurfaceControl(s, "Background for - " + name,
+                mTmpContainerRect.width(), mTmpContainerRect.height(), PixelFormat.OPAQUE,
+                flags | SurfaceControl.FX_SURFACE_DIM);
+    }
+
+    @Override
+    public void setAlpha(float alpha) {
+        super.setAlpha(alpha);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mBackgroundControl.setAlpha(alpha);
+    }
+
+    @Override
+    public void setLayer(int zorder) {
+        super.setLayer(zorder);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        // TODO: Use setRelativeLayer(Integer.MIN_VALUE) when it's fixed.
+        mBackgroundControl.setLayer(zorder - 1);
+    }
+
+    @Override
+    public void setPosition(float x, float y) {
+        super.setPosition(x, y);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mLastX = x;
+        mLastY = y;
+        updateBgPosition();
+    }
+
+    private void updateBgPosition() {
+        mWindowSurfaceController.getContainerRect(mTmpContainerRect);
+        final Rect winFrame = mWindowSurfaceController.mAnimator.mWin.mFrame;
+        final float offsetX = (mTmpContainerRect.left - winFrame.left) * mLastDsDx;
+        final float offsetY = (mTmpContainerRect.top - winFrame.top) * mLastDsDy;
+        mBackgroundControl.setPosition(mLastX + offsetX, mLastY + offsetY);
+    }
+
+    @Override
+    public void setSize(int w, int h) {
+        super.setSize(w, h);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mLastWidth = w;
+        mLastHeight = h;
+        mWindowSurfaceController.getContainerRect(mTmpContainerRect);
+        mBackgroundControl.setSize(mTmpContainerRect.width(), mTmpContainerRect.height());
+    }
+
+    @Override
+    public void setWindowCrop(Rect crop) {
+        super.setWindowCrop(crop);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        if (crop.width() < mLastWidth || crop.height() < mLastHeight) {
+            // We're animating and cropping window, compute the appropriate crop for background.
+            calculateBgCrop(crop);
+            mBackgroundControl.setWindowCrop(mTmpContainerRect);
+        } else {
+            // When not animating just set crop to container rect.
+            mWindowSurfaceController.getContainerRect(mTmpContainerRect);
+            mBackgroundControl.setWindowCrop(mTmpContainerRect);
+        }
+    }
+
+    @Override
+    public void setFinalCrop(Rect crop) {
+        super.setFinalCrop(crop);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        if (crop.width() < mLastWidth || crop.height() < mLastHeight) {
+            // We're animating and cropping window, compute the appropriate crop for background.
+            calculateBgCrop(crop);
+            mBackgroundControl.setFinalCrop(mTmpContainerRect);
+        } else {
+            // When not animating just set crop to container rect.
+            mWindowSurfaceController.getContainerRect(mTmpContainerRect);
+            mBackgroundControl.setFinalCrop(mTmpContainerRect);
+        }
+    }
+
+    /** Compute background crop based on current animation progress for main surface control. */
+    private void calculateBgCrop(Rect crop) {
+        // Track overall progress of animation by computing cropped portion of status bar.
+        final Rect contentInsets = mWindowSurfaceController.mAnimator.mWin.mContentInsets;
+        float d = contentInsets.top == 0 ? 0 : (float) crop.top / contentInsets.top;
+
+        // Compute additional offset for the background when app window is positioned not at (0,0).
+        // E.g. landscape with navigation bar on the left.
+        final Rect winFrame = mWindowSurfaceController.mAnimator.mWin.mFrame;
+        final int offsetX = (int) (winFrame.left * mLastDsDx * d + 0.5);
+        final int offsetY = (int) (winFrame.top * mLastDsDy * d + 0.5);
+
+        // Compute new scaled width and height for background that will depend on current animation
+        // progress. Those consist of current crop rect for the main surface + scaled areas outside
+        // of letterboxed area.
+        mWindowSurfaceController.getContainerRect(mTmpContainerRect);
+        final int backgroundWidth =
+                (int) (crop.width() + (mTmpContainerRect.width() - mLastWidth) * (1 - d) + 0.5);
+        final int backgroundHeight =
+                (int) (crop.height() + (mTmpContainerRect.height() - mLastHeight) * (1 - d) + 0.5);
+
+        mTmpContainerRect.set(crop);
+        // Make sure that part of background to left/top is visible and scaled.
+        mTmpContainerRect.offset(offsetX, offsetY);
+        // Set correct width/height, so that area to right/bottom is cropped properly.
+        mTmpContainerRect.right = mTmpContainerRect.left + backgroundWidth;
+        mTmpContainerRect.bottom = mTmpContainerRect.top + backgroundHeight;
+    }
+
+    @Override
+    public void setLayerStack(int layerStack) {
+        super.setLayerStack(layerStack);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mBackgroundControl.setLayerStack(layerStack);
+    }
+
+    @Override
+    public void setOpaque(boolean isOpaque) {
+        super.setOpaque(isOpaque);
+        mOpaque = isOpaque;
+        updateBackgroundVisibility();
+    }
+
+    @Override
+    public void setSecure(boolean isSecure) {
+        super.setSecure(isSecure);
+    }
+
+    @Override
+    public void setMatrix(float dsdx, float dtdx, float dtdy, float dsdy) {
+        super.setMatrix(dsdx, dtdx, dtdy, dsdy);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mBackgroundControl.setMatrix(dsdx, dtdx, dtdy, dsdy);
+        mLastDsDx = dsdx;
+        mLastDsDy = dsdy;
+        updateBgPosition();
+    }
+
+    @Override
+    public void hide() {
+        super.hide();
+        mVisible = false;
+        updateBackgroundVisibility();
+    }
+
+    @Override
+    public void show() {
+        super.show();
+        mVisible = true;
+        updateBackgroundVisibility();
+    }
+
+    @Override
+    public void destroy() {
+        super.destroy();
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mBackgroundControl.destroy();
+    }
+
+    @Override
+    public void release() {
+        super.release();
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mBackgroundControl.release();
+    }
+
+    @Override
+    public void setTransparentRegionHint(Region region) {
+        super.setTransparentRegionHint(region);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mBackgroundControl.setTransparentRegionHint(region);
+    }
+
+    @Override
+    public void deferTransactionUntil(IBinder handle, long frame) {
+        super.deferTransactionUntil(handle, frame);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mBackgroundControl.deferTransactionUntil(handle, frame);
+    }
+
+    @Override
+    public void deferTransactionUntil(Surface barrier, long frame) {
+        super.deferTransactionUntil(barrier, frame);
+
+        if (mBackgroundControl == null) {
+            return;
+        }
+        mBackgroundControl.deferTransactionUntil(barrier, frame);
+    }
+
+    private void updateBackgroundVisibility() {
+        if (mBackgroundControl == null) {
+            return;
+        }
+        if (mOpaque && mVisible) {
+            mBackgroundControl.show();
+        } else {
+            mBackgroundControl.hide();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 0c68e2c..c58212c 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -133,7 +133,7 @@
         }
 
         @Override
-        public void onInputEvent(InputEvent event) {
+        public void onInputEvent(InputEvent event, int displayId) {
             if (!(event instanceof MotionEvent)
                     || (event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == 0) {
                 return;
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index dd9ba73..42a2d9d 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -24,6 +24,7 @@
 
 import com.android.server.wm.WindowManagerService.H;
 
+import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.PointerIcon.TYPE_NOT_SPECIFIED;
 import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
 import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
@@ -45,6 +46,13 @@
     }
 
     @Override
+    public void onPointerEvent(MotionEvent motionEvent, int displayId) {
+        if (displayId == getDisplayId()) {
+            onPointerEvent(motionEvent);
+        }
+    }
+
+    @Override
     public void onPointerEvent(MotionEvent motionEvent) {
         final int action = motionEvent.getAction();
         switch (action & MotionEvent.ACTION_MASK) {
@@ -104,4 +112,8 @@
            mTouchExcludeRegion.set(newRegion);
         }
     }
+
+    private int getDisplayId() {
+        return mDisplayContent.getDisplayId();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5db691e..83926af 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -79,6 +79,7 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_BOOT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DRAG;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
@@ -357,6 +358,8 @@
 
     final private KeyguardDisableHandler mKeyguardDisableHandler;
     boolean mKeyguardGoingAway;
+    // VR Vr2d Display Id.
+    int mVr2dDisplayId = INVALID_DISPLAY;
 
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
@@ -767,7 +770,7 @@
         }
 
         @Override
-        public void onInputEvent(InputEvent event) {
+        public void onInputEvent(InputEvent event, int displayId) {
             boolean handled = false;
             try {
                 if (mDragState == null) {
@@ -7565,6 +7568,16 @@
                 accessibilityController.performComputeChangedWindowsNotLocked();
             }
         }
+
+        @Override
+        public void setVr2dDisplayId(int vr2dDisplayId) {
+            if (DEBUG_DISPLAY) {
+                Slog.d(TAG, "setVr2dDisplayId called for: " + vr2dDisplayId);
+            }
+            synchronized (WindowManagerService.this) {
+                mVr2dDisplayId = vr2dDisplayId;
+            }
+        }
     }
 
     void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 7decb11..90a2892 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1224,8 +1224,9 @@
         // TODO(b/62846907): Checking against {@link mLastReportedConfiguration} could be flaky as
         //                   this is not necessarily what the client has processed yet. Find a
         //                   better indicator consistent with the client.
-        return mOrientationChanging || (isVisible()
-                && getConfiguration().orientation != mLastReportedConfiguration.orientation);
+        return (mOrientationChanging || (isVisible()
+                && getConfiguration().orientation != mLastReportedConfiguration.orientation))
+                && !mSeamlesslyRotated;
     }
 
     void setOrientationChanging(boolean changing) {
@@ -1462,8 +1463,18 @@
     @Override
     public boolean canAffectSystemUiFlags() {
         final boolean shown = mWinAnimator.getShown();
-        final boolean exiting = mAnimatingExit || mDestroying
-                || mAppToken != null && mAppToken.hidden;
+
+        // We only consider the app to be exiting when the animation has started. After the app
+        // transition is executed the windows are marked exiting before the new windows have been
+        // shown. Thus, wait considering a window to be exiting after the animation has actually
+        // started.
+        final boolean appAnimationStarting = mAppToken != null
+                && mAppToken.mAppAnimator.isAnimationStarting();
+        final boolean exitingSelf = mAnimatingExit && (!mWinAnimator.isAnimationStarting()
+                && !appAnimationStarting);
+        final boolean appExiting = mAppToken != null && mAppToken.hidden && !appAnimationStarting;
+
+        final boolean exiting = exitingSelf || mDestroying || appExiting;
         final boolean translucent = mAttrs.alpha == 0.0f;
         return shown && !exiting && !translucent;
     }
@@ -2031,6 +2042,11 @@
         if (dc == null) {
             return;
         }
+
+        // If layout is currently deferred, we want to hold of with updating the layers.
+        if (mService.mWindowPlacerLocked.isLayoutDeferred()) {
+            return;
+        }
         final DimLayer.DimLayerUser dimLayerUser = getDimLayerUser();
         if (dimLayerUser != null && dc.mDimLayerController.isDimming(dimLayerUser, mWinAnimator)) {
             // Force an animation pass just to update the mDimLayer layer.
@@ -2043,7 +2059,7 @@
             super(inputChannel, mService.mH.getLooper());
         }
         @Override
-        public void onInputEvent(InputEvent event) {
+        public void onInputEvent(InputEvent event, int displayId) {
             finishInputEvent(event, true);
         }
     }
@@ -3228,6 +3244,15 @@
         return !isInMultiWindowMode();
     }
 
+    /** @return true when the window is in fullscreen task, but has non-fullscreen bounds set. */
+    boolean isLetterboxedAppWindow() {
+        final Task task = getTask();
+        final boolean taskIsFullscreen = task != null && task.isFullscreen();
+        final boolean appWindowIsFullscreen = mAppToken != null && !mAppToken.hasBounds();
+
+        return taskIsFullscreen && !appWindowIsFullscreen;
+    }
+
     /** Returns the appropriate bounds to use for computing frames. */
     private void getContainerBounds(Rect outBounds) {
         if (isInMultiWindowMode()) {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 23b515e..86265c29 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -20,6 +20,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
 import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
@@ -631,6 +632,10 @@
             return mSurfaceController;
         }
 
+        if ((mWin.mAttrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0) {
+            windowType = SurfaceControl.WINDOW_TYPE_DONT_SCREENSHOT;
+        }
+
         w.setHasSurface(false);
 
         if (DEBUG_ANIM || DEBUG_ORIENTATION) Slog.i(TAG,
@@ -1195,7 +1200,8 @@
         if (DEBUG_WINDOW_CROP) Slog.d(TAG, "Applying decor to crop win=" + w + " mDecorFrame="
                 + w.mDecorFrame + " mSystemDecorRect=" + mSystemDecorRect);
 
-        final boolean fullscreen = w.fillsDisplay();
+        final Task task = w.getTask();
+        final boolean fullscreen = w.fillsDisplay() || (task != null && task.isFullscreen());
         final boolean isFreeformResizing =
                 w.isDragResizing() && w.getResizeMode() == DRAG_RESIZE_MODE_FREEFORM;
 
@@ -1521,6 +1527,19 @@
         }
     }
 
+    /**
+     * Get rect of the task this window is currently in. If there is no task, rect will be set to
+     * empty.
+     */
+    void getContainerRect(Rect rect) {
+        final Task task = mWin.getTask();
+        if (task != null) {
+            task.getDimBounds(rect);
+        } else {
+            rect.left = rect.top = rect.right = rect.bottom = 0;
+        }
+    }
+
     void prepareSurfaceLocked(final boolean recoveringMemory) {
         final WindowState w = mWin;
         if (!hasSurface()) {
@@ -1627,9 +1646,14 @@
                 // hidden while the screen is turning off.
                 // TODO(b/63773439): These cases should be eliminated, though we probably still
                 // want to process mTurnOnScreen in this way for clarity.
-                if (mWin.mTurnOnScreen) {
+                if (mWin.mTurnOnScreen && mWin.mAppToken.canTurnScreenOn()) {
                     if (DEBUG_VISIBILITY) Slog.v(TAG, "Show surface turning screen on: " + mWin);
                     mWin.mTurnOnScreen = false;
+
+                    // The window should only turn the screen on once per resume, but
+                    // prepareSurfaceLocked can be called multiple times. Set canTurnScreenOn to
+                    // false so the window doesn't turn the screen on again during this resume.
+                    mWin.mAppToken.setCanTurnScreenOn(false);
                     mAnimator.mBulkUpdateParams |= SET_TURN_ON_SCREEN;
                 }
             }
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index 1728cfb..110d5cb 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -50,7 +50,7 @@
 
     final WindowStateAnimator mAnimator;
 
-    private SurfaceControl mSurfaceControl;
+    private SurfaceControlWithBackground mSurfaceControl;
 
     // Should only be set from within setShown().
     private boolean mSurfaceShown = false;
@@ -97,15 +97,10 @@
         mWindowType = windowType;
         mWindowSession = win.mSession;
 
-        if (DEBUG_SURFACE_TRACE) {
-            mSurfaceControl = new SurfaceTrace(
-                    s, name, w, h, format, flags, windowType, ownerUid);
-        } else {
-            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
-            mSurfaceControl = new SurfaceControl(
-                    s, name, w, h, format, flags, windowType, ownerUid);
-            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-        }
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
+        mSurfaceControl = new SurfaceControlWithBackground(
+                s, name, w, h, format, flags, windowType, ownerUid, this);
+        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
 
         if (mService.mRoot.mSurfaceTraceEnabled) {
             mSurfaceControl = new RemoteSurfaceTrace(
@@ -118,7 +113,7 @@
     }
 
     void removeRemoteTrace() {
-        mSurfaceControl = new SurfaceControl(mSurfaceControl);
+        mSurfaceControl = new SurfaceControlWithBackground(mSurfaceControl);
     }
 
 
@@ -291,30 +286,30 @@
         mSurfaceControl.setGeometryAppliesWithResize();
     }
 
-    void setMatrixInTransaction(float dsdx, float dtdx, float dsdy, float dtdy,
+    void setMatrixInTransaction(float dsdx, float dtdx, float dtdy, float dsdy,
             boolean recoveringMemory) {
         final boolean matrixChanged = mLastDsdx != dsdx || mLastDtdx != dtdx ||
-                                      mLastDsdy != dsdy || mLastDtdy != dtdy;
+                                      mLastDtdy != dtdy || mLastDsdy != dsdy;
         if (!matrixChanged) {
             return;
         }
 
         mLastDsdx = dsdx;
         mLastDtdx = dtdx;
-        mLastDsdy = dsdy;
         mLastDtdy = dtdy;
+        mLastDsdy = dsdy;
 
         try {
             if (SHOW_TRANSACTIONS) logSurface(
-                    "MATRIX [" + dsdx + "," + dtdx + "," + dsdy + "," + dtdy + "]", null);
+                    "MATRIX [" + dsdx + "," + dtdx + "," + dtdy + "," + dsdy + "]", null);
             mSurfaceControl.setMatrix(
-                    dsdx, dtdx, dsdy, dtdy);
+                    dsdx, dtdx, dtdy, dsdy);
         } catch (RuntimeException e) {
             // If something goes wrong with the surface (such
             // as running out of memory), don't take down the
             // entire system.
             Slog.e(TAG, "Error setting matrix on surface surface" + title
-                    + " MATRIX [" + dsdx + "," + dtdx + "," + dsdy + "," + dtdy + "]", null);
+                    + " MATRIX [" + dsdx + "," + dtdx + "," + dtdy + "," + dsdy + "]", null);
             if (!recoveringMemory) {
                 mAnimator.reclaimSomeSurfaceMemory("matrix", true);
             }
@@ -421,6 +416,10 @@
         }
     }
 
+    void getContainerRect(Rect rect) {
+        mAnimator.getContainerRect(rect);
+    }
+
     boolean showRobustlyInTransaction() {
         if (SHOW_TRANSACTIONS) logSurface(
                 "SHOW (performLayout)", null);
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 6909892..581b044 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -127,6 +127,10 @@
         }
     }
 
+    boolean isLayoutDeferred() {
+        return mDeferDepth > 0;
+    }
+
     final void performSurfacePlacement() {
         performSurfacePlacement(false /* force */);
     }
diff --git a/services/core/jni/BroadcastRadio/Tuner.cpp b/services/core/jni/BroadcastRadio/Tuner.cpp
index f5a85c1..85603d5 100644
--- a/services/core/jni/BroadcastRadio/Tuner.cpp
+++ b/services/core/jni/BroadcastRadio/Tuner.cpp
@@ -296,7 +296,7 @@
 
     auto selector = convert::ProgramSelectorToHal(env, jSelector);
     if (halTuner11 != nullptr) {
-        convert::ThrowIfFailed(env, halTuner11->tune_1_1(selector));
+        convert::ThrowIfFailed(env, halTuner11->tuneByProgramSelector(selector));
     } else {
         uint32_t channel, subChannel;
         if (!V1_1::utils::getLegacyChannel(selector, &channel, &subChannel)) {
@@ -374,7 +374,7 @@
     return !convert::ThrowIfFailed(env, halResult);
 }
 
-static jobject nativeGetProgramList(JNIEnv *env, jobject obj, jlong nativeContext, jstring jFilter) {
+static jobject nativeGetProgramList(JNIEnv *env, jobject obj, jlong nativeContext, jobject jVendorFilter) {
     ALOGV("%s", __func__);
     auto halTuner = getHalTuner11(nativeContext);
     if (halTuner == nullptr) {
@@ -384,7 +384,7 @@
 
     JavaRef<jobject> jList;
     ProgramListResult halResult = ProgramListResult::NOT_INITIALIZED;
-    auto filter = env->GetStringUTFChars(jFilter, nullptr);
+    auto filter = convert::VendorInfoToHal(env, jVendorFilter);
     auto hidlResult = halTuner->getProgramList(filter,
             [&](ProgramListResult result, const hidl_vec<V1_1::ProgramInfo>& programList) {
         halResult = result;
@@ -505,7 +505,7 @@
     { "nativeGetProgramInformation", "(J)Landroid/hardware/radio/RadioManager$ProgramInfo;",
             (void*)nativeGetProgramInformation },
     { "nativeStartBackgroundScan", "(J)Z", (void*)nativeStartBackgroundScan },
-    { "nativeGetProgramList", "(JLjava/lang/String;)Ljava/util/List;",
+    { "nativeGetProgramList", "(JLjava/util/Map;)Ljava/util/List;",
             (void*)nativeGetProgramList },
     { "nativeGetImage", "(JI)[B", (void*)nativeGetImage},
     { "nativeIsAnalogForced", "(J)Z", (void*)nativeIsAnalogForced },
diff --git a/services/core/jni/BroadcastRadio/TunerCallback.cpp b/services/core/jni/BroadcastRadio/TunerCallback.cpp
index 04bdddf..d53721f 100644
--- a/services/core/jni/BroadcastRadio/TunerCallback.cpp
+++ b/services/core/jni/BroadcastRadio/TunerCallback.cpp
@@ -46,6 +46,7 @@
 using V1_0::MetaData;
 using V1_0::Result;
 using V1_1::ITunerCallback;
+using V1_1::ProgramInfo;
 using V1_1::ProgramListResult;
 using V1_1::ProgramSelector;
 
@@ -58,7 +59,7 @@
         jmethodID handleHwFailure;
         jmethodID onError;
         jmethodID onConfigurationChanged;
-        jmethodID onProgramInfoChanged;
+        jmethodID onCurrentProgramInfoChanged;
         jmethodID onTrafficAnnouncement;
         jmethodID onEmergencyAnnouncement;
         jmethodID onAntennaState;
@@ -82,6 +83,8 @@
 static mutex gContextMutex;
 
 class NativeCallback : public ITunerCallback {
+    mutex mMut;
+
     jobject mJTuner;
     jobject mJCallback;
     NativeCallbackThread mCallbackThread;
@@ -89,6 +92,9 @@
 
     Band mBand;
 
+    // Carries current program info data for 1.0 newMetadata callback.
+    V1_0::ProgramInfo mCurrentProgramInfo;
+
     DISALLOW_COPY_AND_ASSIGN(NativeCallback);
 
 public:
@@ -107,11 +113,10 @@
     virtual Return<void> newMetadata(uint32_t channel, uint32_t subChannel,
             const hidl_vec<MetaData>& metadata);
     virtual Return<void> tuneComplete_1_1(Result result, const ProgramSelector& selector);
-    virtual Return<void> afSwitch_1_1(const ProgramSelector& selector);
     virtual Return<void> backgroundScanAvailable(bool isAvailable);
     virtual Return<void> backgroundScanComplete(ProgramListResult result);
     virtual Return<void> programListChanged();
-    virtual Return<void> programInfoChanged();
+    virtual Return<void> currentProgramInfoChanged(const ProgramInfo& info);
 };
 
 struct TunerCallbackContext {
@@ -180,7 +185,22 @@
 
     if (mHalRev > HalRevision::V1_0) {
         ALOGW("1.0 callback was ignored");
-        return Return<void>();
+        return {};
+    }
+
+    if (result == Result::OK) {
+        {
+            lock_guard<mutex> lk(mMut);
+            mCurrentProgramInfo = info;
+        }
+
+        // tuneComplete_1_1 implementation does not handle success case, see the implementation
+        mCallbackThread.enqueue([this, info](JNIEnv *env) {
+            auto jInfo = convert::ProgramInfoFromHal(env, info, mBand);
+            env->CallVoidMethod(mJCallback, gjni.TunerCallback.onCurrentProgramInfoChanged,
+                    jInfo.get());
+        });
+        return {};
     }
 
     auto selector = V1_1::utils::make_selector(mBand, info.channel, info.subChannel);
@@ -191,13 +211,14 @@
     ALOGV("%s(%d)", __func__, result);
 
     mCallbackThread.enqueue([result, this](JNIEnv *env) {
-        if (result == Result::OK) {
-            env->CallVoidMethod(mJCallback, gjni.TunerCallback.onProgramInfoChanged);
-        } else {
-            TunerError cause = TunerError::CANCELLED;
-            if (result == Result::TIMEOUT) cause = TunerError::SCAN_TIMEOUT;
-            env->CallVoidMethod(mJCallback, gjni.TunerCallback.onError, cause);
-        }
+        /* for HAL 1.1, onCurrentProgramInfoChanged will be called from currentProgramInfoChanged,
+         * so we don't need to handle success case here.
+         */
+        if (result == Result::OK) return;
+
+        TunerError cause = TunerError::CANCELLED;
+        if (result == Result::TIMEOUT) cause = TunerError::SCAN_TIMEOUT;
+        env->CallVoidMethod(mJCallback, gjni.TunerCallback.onError, cause);
     });
 
     return Return<void>();
@@ -208,11 +229,6 @@
     return tuneComplete(Result::OK, info);
 }
 
-Return<void> NativeCallback::afSwitch_1_1(const ProgramSelector& selector) {
-    ALOGV("%s", __func__);
-    return tuneComplete_1_1(Result::OK, selector);
-}
-
 Return<void> NativeCallback::antennaStateChange(bool connected) {
     ALOGV("%s(%d)", __func__, connected);
 
@@ -245,19 +261,32 @@
 
 Return<void> NativeCallback::newMetadata(uint32_t channel, uint32_t subChannel,
         const hidl_vec<MetaData>& metadata) {
-    // channel and subChannel are not used
     ALOGV("%s(%d, %d)", __func__, channel, subChannel);
 
     if (mHalRev > HalRevision::V1_0) {
         ALOGW("1.0 callback was ignored");
-        return Return<void>();
+        return {};
     }
 
-    mCallbackThread.enqueue([this, metadata](JNIEnv *env) {
-        env->CallVoidMethod(mJCallback, gjni.TunerCallback.onProgramInfoChanged);
+    V1_0::ProgramInfo info;
+    {
+        lock_guard<mutex> lk(mMut);
+        info = mCurrentProgramInfo;
+    }
+    if (channel != info.channel || subChannel != info.subChannel) {
+        ALOGE("Channel mismatch on newMetadata callback (%d.%d != %d.%d)",
+                channel, subChannel, info.channel, info.subChannel);
+        return {};
+    }
+    info.metadata = metadata;
+
+    mCallbackThread.enqueue([this, info](JNIEnv *env) {
+        auto jInfo = convert::ProgramInfoFromHal(env, info, mBand);
+        env->CallVoidMethod(mJCallback, gjni.TunerCallback.onCurrentProgramInfoChanged,
+                jInfo.get());
     });
 
-    return Return<void>();
+    return {};
 }
 
 Return<void> NativeCallback::backgroundScanAvailable(bool isAvailable) {
@@ -297,11 +326,13 @@
     return Return<void>();
 }
 
-Return<void> NativeCallback::programInfoChanged() {
-    ALOGV("%s", __func__);
+Return<void> NativeCallback::currentProgramInfoChanged(const ProgramInfo& info) {
+    ALOGV("%s(%s)", __func__, toString(info).substr(0, 100).c_str());
 
-    mCallbackThread.enqueue([this](JNIEnv *env) {
-        env->CallVoidMethod(mJCallback, gjni.TunerCallback.onProgramInfoChanged);
+    mCallbackThread.enqueue([this, info](JNIEnv *env) {
+        auto jInfo = convert::ProgramInfoFromHal(env, info);
+        env->CallVoidMethod(mJCallback, gjni.TunerCallback.onCurrentProgramInfoChanged,
+                jInfo.get());
     });
 
     return Return<void>();
@@ -379,8 +410,8 @@
     gjni.TunerCallback.onError = GetMethodIDOrDie(env, tunerCbClass, "onError", "(I)V");
     gjni.TunerCallback.onConfigurationChanged = GetMethodIDOrDie(env, tunerCbClass,
             "onConfigurationChanged", "(Landroid/hardware/radio/RadioManager$BandConfig;)V");
-    gjni.TunerCallback.onProgramInfoChanged = GetMethodIDOrDie(env, tunerCbClass,
-            "onProgramInfoChanged", "()V");
+    gjni.TunerCallback.onCurrentProgramInfoChanged = GetMethodIDOrDie(env, tunerCbClass,
+            "onCurrentProgramInfoChanged", "(Landroid/hardware/radio/RadioManager$ProgramInfo;)V");
     gjni.TunerCallback.onTrafficAnnouncement = GetMethodIDOrDie(env, tunerCbClass,
             "onTrafficAnnouncement", "(Z)V");
     gjni.TunerCallback.onEmergencyAnnouncement = GetMethodIDOrDie(env, tunerCbClass,
diff --git a/services/core/jni/BroadcastRadio/convert.cpp b/services/core/jni/BroadcastRadio/convert.cpp
index ba1395f73..3e0bc63 100644
--- a/services/core/jni/BroadcastRadio/convert.cpp
+++ b/services/core/jni/BroadcastRadio/convert.cpp
@@ -41,6 +41,7 @@
 using V1_1::ProgramIdentifier;
 using V1_1::ProgramListResult;
 using V1_1::ProgramSelector;
+using V1_1::VendorKeyValue;
 
 static JavaRef<jobject> BandDescriptorFromHal(JNIEnv *env, const V1_0::BandConfig &config, Region region);
 
@@ -82,6 +83,20 @@
 
     struct {
         jclass clazz;
+        jmethodID stringMapToNative;
+    } Convert;
+
+    struct {
+        jclass clazz;
+        jmethodID cstor;
+    } HashMap;
+
+    struct {
+        jmethodID put;
+    } Map;
+
+    struct {
+        jclass clazz;
         jmethodID cstor;
     } ModuleProperties;
 
@@ -228,6 +243,53 @@
             std::function<JavaRef<jobject>(JNIEnv*, const T&)>(converter));
 }
 
+static std::string StringFromJava(JNIEnv *env, JavaRef<jstring> &jStr) {
+    auto cstr = (jStr == nullptr) ? nullptr : env->GetStringUTFChars(jStr.get(), nullptr);
+    std::string str(cstr);
+    env->ReleaseStringUTFChars(jStr.get(), cstr);
+    return str;
+}
+
+JavaRef<jobject> VendorInfoFromHal(JNIEnv *env, const hidl_vec<VendorKeyValue> &info) {
+    ALOGV("%s(%s)", __func__, toString(info).substr(0, 100).c_str());
+
+    auto jInfo = make_javaref(env, env->NewObject(gjni.HashMap.clazz, gjni.HashMap.cstor));
+
+    for (auto&& entry : info) {
+        auto jKey = make_javastr(env, entry.key);
+        auto jValue = make_javastr(env, entry.value);
+        env->CallObjectMethod(jInfo.get(), gjni.Map.put, jKey.get(), jValue.get());
+    }
+
+    return jInfo;
+}
+
+hidl_vec<VendorKeyValue> VendorInfoToHal(JNIEnv *env, jobject jInfo) {
+    ALOGV("%s", __func__);
+
+    auto jInfoArr = make_javaref(env, static_cast<jobjectArray>(env->CallStaticObjectMethod(
+            gjni.Convert.clazz, gjni.Convert.stringMapToNative, jInfo)));
+    LOG_FATAL_IF(jInfoArr == nullptr, "Converted array is null");
+
+    auto len = env->GetArrayLength(jInfoArr.get());
+    hidl_vec<VendorKeyValue> vec;
+    vec.resize(len);
+
+    for (jsize i = 0; i < len; i++) {
+        auto entry = make_javaref(env, static_cast<jobjectArray>(
+                env->GetObjectArrayElement(jInfoArr.get(), i)));
+        auto jKey = make_javaref(env, static_cast<jstring>(
+                env->GetObjectArrayElement(entry.get(), 0)));
+        auto jValue = make_javaref(env, static_cast<jstring>(
+                env->GetObjectArrayElement(entry.get(), 1)));
+        auto key = StringFromJava(env, jKey);
+        auto value = StringFromJava(env, jValue);
+        vec[i] = { key, value };
+    }
+
+    return vec;
+}
+
 static Rds RdsForRegion(bool rds, Region region) {
     if (!rds) return Rds::NONE;
 
@@ -271,7 +333,7 @@
     auto jVersion = make_javastr(env, prop10.version);
     auto jSerial = make_javastr(env, prop10.serial);
     bool isBgScanSupported = prop11 ? prop11->supportsBackgroundScanning : false;
-    auto jVendorInfo = prop11 ? make_javastr(env, prop11->vendorInfo) : nullptr;
+    auto jVendorInfo = prop11 ? VendorInfoFromHal(env, prop11->vendorInfo) : nullptr;
     // ITU_1 is the default region just because its index is 0.
     auto jBands = ArrayFromHal<V1_0::BandConfig>(env, prop10.bands, gjni.BandDescriptor.clazz,
         std::bind(BandDescriptorFromHal, _1, _2, Region::ITU_1));
@@ -512,7 +574,7 @@
     ALOGV("%s", __func__);
 
     auto jMetadata = MetadataFromHal(env, info10.metadata);
-    auto jVendorInfo = info11 ? make_javastr(env, info11->vendorInfo) : nullptr;
+    auto jVendorInfo = info11 ? VendorInfoFromHal(env, info11->vendorInfo) : nullptr;
     auto jSelector = ProgramSelectorFromHal(env, selector);
 
     return make_javaref(env, env->NewObject(gjni.ProgramInfo.clazz, gjni.ProgramInfo.cstor,
@@ -579,19 +641,32 @@
     gjni.AmBandDescriptor.cstor = GetMethodIDOrDie(env, amBandDescriptorClass,
             "<init>", "(IIIIIZ)V");
 
+    auto convertClass = FindClassOrDie(env, "com/android/server/broadcastradio/Convert");
+    gjni.Convert.clazz = MakeGlobalRefOrDie(env, convertClass);
+    gjni.Convert.stringMapToNative = GetStaticMethodIDOrDie(env, convertClass, "stringMapToNative",
+            "(Ljava/util/Map;)[[Ljava/lang/String;");
+
+    auto hashMapClass = FindClassOrDie(env, "java/util/HashMap");
+    gjni.HashMap.clazz = MakeGlobalRefOrDie(env, hashMapClass);
+    gjni.HashMap.cstor = GetMethodIDOrDie(env, hashMapClass, "<init>", "()V");
+
+    auto mapClass = FindClassOrDie(env, "java/util/Map");
+    gjni.Map.put = GetMethodIDOrDie(env, mapClass, "put",
+            "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+
     auto modulePropertiesClass = FindClassOrDie(env,
             "android/hardware/radio/RadioManager$ModuleProperties");
     gjni.ModuleProperties.clazz = MakeGlobalRefOrDie(env, modulePropertiesClass);
     gjni.ModuleProperties.cstor = GetMethodIDOrDie(env, modulePropertiesClass, "<init>",
             "(ILjava/lang/String;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;"
             "Ljava/lang/String;IIZ[Landroid/hardware/radio/RadioManager$BandDescriptor;Z"
-            "[I[ILjava/lang/String;)V");
+            "[I[ILjava/util/Map;)V");
 
     auto programInfoClass = FindClassOrDie(env, "android/hardware/radio/RadioManager$ProgramInfo");
     gjni.ProgramInfo.clazz = MakeGlobalRefOrDie(env, programInfoClass);
     gjni.ProgramInfo.cstor = GetMethodIDOrDie(env, programInfoClass, "<init>",
             "(Landroid/hardware/radio/ProgramSelector;ZZZILandroid/hardware/radio/RadioMetadata;I"
-            "Ljava/lang/String;)V");
+            "Ljava/util/Map;)V");
 
     auto programSelectorClass = FindClassOrDie(env, "android/hardware/radio/ProgramSelector");
     gjni.ProgramSelector.clazz = MakeGlobalRefOrDie(env, programSelectorClass);
diff --git a/services/core/jni/BroadcastRadio/convert.h b/services/core/jni/BroadcastRadio/convert.h
index 198e594..1fc75f0 100644
--- a/services/core/jni/BroadcastRadio/convert.h
+++ b/services/core/jni/BroadcastRadio/convert.h
@@ -35,6 +35,9 @@
 namespace V1_0 = hardware::broadcastradio::V1_0;
 namespace V1_1 = hardware::broadcastradio::V1_1;
 
+JavaRef<jobject> VendorInfoFromHal(JNIEnv *env, const hardware::hidl_vec<V1_1::VendorKeyValue> &info);
+hardware::hidl_vec<V1_1::VendorKeyValue> VendorInfoToHal(JNIEnv *env, jobject jInfo);
+
 JavaRef<jobject> ModulePropertiesFromHal(JNIEnv *env, const V1_0::Properties &properties,
         jint moduleId, const std::string& serviceName);
 JavaRef<jobject> ModulePropertiesFromHal(JNIEnv *env, const V1_1::Properties &properties,
diff --git a/services/core/jni/com_android_server_ConsumerIrService.cpp b/services/core/jni/com_android_server_ConsumerIrService.cpp
index e9c944d..2ca348b 100644
--- a/services/core/jni/com_android_server_ConsumerIrService.cpp
+++ b/services/core/jni/com_android_server_ConsumerIrService.cpp
@@ -17,14 +17,14 @@
 #define LOG_TAG "ConsumerIrService"
 
 #include "jni.h"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
 #include "android_runtime/AndroidRuntime.h"
 
 #include <stdlib.h>
 #include <utils/misc.h>
 #include <utils/Log.h>
 #include <android/hardware/ir/1.0/IConsumerIr.h>
-#include <ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
 
 using ::android::hardware::ir::V1_0::IConsumerIr;
 using ::android::hardware::ir::V1_0::ConsumerIrFreqRange;
diff --git a/services/core/jni/com_android_server_GraphicsStatsService.cpp b/services/core/jni/com_android_server_GraphicsStatsService.cpp
index 61c53e6..8385020 100644
--- a/services/core/jni/com_android_server_GraphicsStatsService.cpp
+++ b/services/core/jni/com_android_server_GraphicsStatsService.cpp
@@ -19,8 +19,8 @@
 #include <jni.h>
 #include <log/log.h>
 #include <nativehelper/JNIHelp.h>
-#include <ScopedPrimitiveArray.h>
-#include <ScopedUtfChars.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
 #include <JankTracker.h>
 #include <service/GraphicsStatsService.h>
 
diff --git a/services/core/jni/com_android_server_location_ContextHubService.cpp b/services/core/jni/com_android_server_location_ContextHubService.cpp
index d59bc10..d90b011 100644
--- a/services/core/jni/com_android_server_location_ContextHubService.cpp
+++ b/services/core/jni/com_android_server_location_ContextHubService.cpp
@@ -38,7 +38,7 @@
 #include <cutils/log.h>
 
 #include "core_jni_helpers.h"
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
 
 using android::hardware::contexthub::V1_0::AsyncEventType;
 using android::hardware::contexthub::V1_0::ContextHub;
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 4fb2ae3..1f1324a 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -20,7 +20,7 @@
 
 #include <android/hardware/gnss/1.0/IGnss.h>
 
-#include "JNIHelp.h"
+#include <nativehelper/JNIHelp.h>
 #include "jni.h"
 #include "hardware_legacy/power.h"
 #include "utils/Log.h"
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 2db7dbe..39f90ca 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -22,7 +22,7 @@
 #include "JNIHelp.h"
 #include "jni.h"
 
-#include <ScopedUtfChars.h>
+#include <nativehelper/ScopedUtfChars.h>
 
 #include <limits.h>
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d67bf63..a1b9099 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -287,6 +287,15 @@
                 SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);
             }
 
+            //
+            // Default the timezone property to GMT if not set.
+            //
+            String timezoneProperty =  SystemProperties.get("persist.sys.timezone");
+            if (timezoneProperty == null || timezoneProperty.isEmpty()) {
+                Slog.w(TAG, "Timezone not set; setting to GMT.");
+                SystemProperties.set("persist.sys.timezone", "GMT");
+            }
+
             // If the system has "persist.sys.language" and friends set, replace them with
             // "persist.sys.locale". Note that the default locale at this point is calculated
             // using the "-Duser.locale" command line flag. That flag is usually populated by
diff --git a/services/net/OWNERS b/services/net/OWNERS
index fa26997..2d71c20 100644
--- a/services/net/OWNERS
+++ b/services/net/OWNERS
@@ -1,6 +1,9 @@
 set noparent
 
 per-file Android.mk = build.master@android.com
+per-file Android.mk = ek@google.com
+per-file Android.mk = hugobenichi@google.com
+per-file Android.mk = lorenzo@google.com
 
 ek@google.com
 hugobenichi@google.com
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 507b483..baec956 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -30,6 +30,7 @@
 LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
 
 LOCAL_SRC_FILES += aidl/com/android/servicestests/aidl/INetworkStateObserver.aidl
+LOCAL_SRC_FILES += $(call all-java-files-under, test-apps/JobTestApp/src)
 
 LOCAL_JAVA_LIBRARIES := android.test.mock legacy-android-test
 
@@ -61,3 +62,5 @@
 LOCAL_STATIC_JAVA_LIBRARIES += ub-uiautomator
 
 include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under, $(LOCAL_PATH))
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 59d205e..4729d06 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -50,6 +50,11 @@
     <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
     <uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
     <uses-permission android:name="android.permission.DELETE_PACKAGES" />
+    <uses-permission android:name="android.permission.GET_APP_OPS_STATS" />
+    <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
+    <uses-permission android:name="android.permission.DEVICE_POWER" />
+    <uses-permission android:name="android.permission.FORCE_STOP_PACKAGES" />
+    <uses-permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST" />
 
     <!-- Uses API introduced in O (26) -->
     <uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index 63f3b75..cea9a8b 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -16,6 +16,7 @@
 <configuration description="Runs Frameworks Services Tests.">
     <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
         <option name="test-file-name" value="FrameworksServicesTests.apk" />
+        <option name="test-file-name" value="JobTestApp.apk" />
     </target_preparer>
 
     <option name="test-suite-tag" value="apct" />
diff --git a/services/tests/servicestests/assets/AppOpsUpgradeTest/appops-unversioned.xml b/services/tests/servicestests/assets/AppOpsUpgradeTest/appops-unversioned.xml
new file mode 100644
index 0000000..a37d84f
--- /dev/null
+++ b/services/tests/servicestests/assets/AppOpsUpgradeTest/appops-unversioned.xml
@@ -0,0 +1,200 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
+<app-ops>
+<uid n="1001">
+<op n="15" m="0" />
+</uid>
+<uid n="10052">
+<op n="63" m="1" />
+</uid>
+<pkg n="com.quicinc.cne.CNEService">
+<uid n="1000" p="true">
+<op n="59" t="1501535978641" pu="0" />
+<op n="60" t="1501535978641" pu="0" />
+</uid>
+</pkg>
+<pkg n="android">
+<uid n="1000" p="true">
+<op n="0" />
+<op n="3" t="1501537828283" d="22" />
+<op n="8" t="1501535987988" pu="0" pp="com.android.providers.calendar" />
+<op n="23" r="1501535979451" />
+<op n="40" t="1501621469584" d="1" />
+<op n="41" t="1501535980608" d="85615033" />
+<op n="61" t="1501557904487" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.android.server.telecom">
+<uid n="1000" p="true">
+<op n="6" t="1501535984350" pu="0" pp="com.android.providers.contacts" />
+</uid>
+</pkg>
+<pkg n="com.android.settings">
+<uid n="1000" p="true">
+<op n="59" t="1501536001265" pu="0" />
+<op n="60" t="1501536001265" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.android.providers.telephony">
+<uid n="1001" p="true">
+<op n="15" m="0" />
+</uid>
+</pkg>
+<pkg n="com.qualcomm.qti.rcsbootstraputil">
+<uid n="1001" p="false">
+<op n="59" t="1501535981233" pu="0" />
+<op n="60" t="1501535981233" pu="0" />
+<op n="63" t="1501536015379" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.android.phone">
+<uid n="1001" p="true">
+<op n="14" t="1501547602479" />
+<op n="15" m="0" t="1501535981903" pu="0" />
+<op n="40" t="1501621220685" d="4" />
+<op n="59" t="1501535978675" pu="0" />
+<op n="60" t="1501535978675" pu="0" />
+<op n="63" m="1" t="1501277487395" pu="0" />
+</uid>
+</pkg>
+<pkg n="audioserver">
+<uid n="1041" p="false">
+<op n="40" t="1501542152888" d="4" />
+</uid>
+</pkg>
+<pkg n="com.android.shell">
+<uid n="2000" p="true">
+<op n="59" t="1501535997600" pu="0" />
+<op n="60" t="1501535997600" pu="0" />
+<op n="63" t="1501535997600" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.google.android.apps.turbo">
+<uid n="10024" p="true">
+<op n="59" t="1501621079685" pu="0" />
+<op n="60" t="1501621079685" pu="0" />
+<op n="63" t="1501621079682" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.android.providers.downloads">
+<uid n="10027" p="true">
+<op n="59" t="1501601386341" />
+<op n="60" t="1501601375992" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.google.android.carriersetup">
+<uid n="10029" p="true">
+<op n="59" t="1501536001405" pu="0" />
+<op n="60" t="1501536001405" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.android.systemui">
+<uid n="10031" p="true">
+<op n="3" t="1501537825972" d="21" />
+<op n="40" t="1501619729317" d="7297" />
+<op n="59" t="1501535979651" pu="0" />
+<op n="60" t="1501535978058" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.android.chrome">
+<uid n="10096" p="false">
+<op n="23" r="1501537723291" />
+<op n="59" t="1501537615416" pu="0" />
+<op n="60" t="1501537615416" pu="0" />
+<op n="63" m="1" />
+</uid>
+</pkg>
+<pkg n="com.google.android.apps.maps">
+<uid n="10102" p="false">
+<op n="0" />
+<op n="1" t="1501620392477" pu="0" />
+<op n="59" t="1501620392609" pu="0" />
+<op n="60" t="1501620392609" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.google.android.syncadapters.contacts">
+<uid n="10109" p="false">
+<op n="4" t="1501535997715" pu="0" pp="com.android.providers.contacts" />
+<op n="59" t="1501535997265" pu="0" />
+<op n="60" t="1501535997265" pu="0" />
+<op n="63" t="1501535997589" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.google.android.youtube">
+<uid n="10111" p="false">
+<op n="59" t="1501620380957" pu="0" />
+<op n="60" t="1501620380957" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.google.android.deskclock">
+<uid n="10114" p="false">
+<op n="40" t="1501537682746" d="379" />
+<op n="59" t="1501537682098" pu="0" />
+<op n="60" t="1501537682098" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.google.android.apps.internal.betterbug">
+<uid n="10117" p="false">
+<op n="59" t="1501535989133" pu="0" />
+<op n="60" t="1501535989133" pu="0" />
+<op n="63" t="1501535989132" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.google.android.tts">
+<uid n="10118" p="false">
+<op n="59" t="1501193966186" pu="0" />
+<op n="60" t="1501193966186" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.google.android.apps.enterprise.dmagent">
+<uid n="10119" p="false">
+<op n="59" t="1501193986104" pu="0" />
+<op n="60" t="1501193986104" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.qualcomm.embms">
+<uid n="10122" p="false">
+<op n="59" t="1501535999723" pu="0" />
+<op n="60" t="1501535999723" pu="0" />
+<op n="63" t="1501535999550" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.qualcomm.qti.telephonyservice">
+<uid n="10123" p="false">
+<op n="59" t="1501535978649" pu="0" />
+<op n="60" t="1501535978649" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.qualcomm.ltebc_vzw">
+<uid n="10124" p="false">
+<op n="59" t="1501536001390" pu="0" />
+<op n="60" t="1501536001390" pu="0" />
+<op n="63" t="1501536000356" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.android.ramdump">
+<uid n="10125" p="false">
+<op n="59" t="1501536047490" pu="0" />
+<op n="60" t="1501536047490" pu="0" />
+<op n="63" m="1" />
+</uid>
+</pkg>
+<pkg n="com.android.nexuslogger">
+<uid n="10127" p="false">
+<op n="59" t="1501535985248" pu="0" />
+<op n="60" t="1501535985248" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.google.android.apps.multidevice.client">
+<uid n="10131" p="false">
+<op n="59" t="1501535991782" pu="0" />
+<op n="60" t="1501535991782" pu="0" />
+<op n="63" t="1501535991781" pu="0" />
+</uid>
+</pkg>
+<pkg n="com.android.frameworks.servicestests">
+<uid n="10132" p="false">
+<op n="59" t="1501551739953" pu="0" />
+<op n="60" t="1501551739953" pu="0" />
+</uid>
+</pkg>
+</app-ops>
diff --git a/services/tests/servicestests/src/com/android/server/AppOpsUpgradeTest.java b/services/tests/servicestests/src/com/android/server/AppOpsUpgradeTest.java
new file mode 100644
index 0000000..cc89ae8
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/AppOpsUpgradeTest.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2017 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.Xml;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Tests app ops version upgrades
+ */
+@RunWith(AndroidJUnit4.class)
+public class AppOpsUpgradeTest {
+    private static final String TAG = AppOpsUpgradeTest.class.getSimpleName();
+    private static final String APP_OPS_UNVERSIONED_ASSET_PATH =
+            "AppOpsUpgradeTest/appops-unversioned.xml";
+    private static final String APP_OPS_FILENAME = "appops-test.xml";
+    private static final int NON_DEFAULT_OPS_IN_FILE = 4;
+    private static final int CURRENT_VERSION = 1;
+
+    private File mAppOpsFile;
+    private Context mContext;
+    private Handler mHandler;
+
+    private void extractAppOpsFile() {
+        mAppOpsFile.getParentFile().mkdirs();
+        if (mAppOpsFile.exists()) {
+            mAppOpsFile.delete();
+        }
+        try (FileOutputStream out = new FileOutputStream(mAppOpsFile);
+             InputStream in = mContext.getAssets().open(APP_OPS_UNVERSIONED_ASSET_PATH,
+                     AssetManager.ACCESS_BUFFER)) {
+            byte[] buffer = new byte[4096];
+            int bytesRead;
+            while ((bytesRead = in.read(buffer)) >= 0) {
+                out.write(buffer, 0, bytesRead);
+            }
+            out.flush();
+            Log.d(TAG, "Successfully copied xml to " + mAppOpsFile.getAbsolutePath());
+        } catch (IOException exc) {
+            Log.e(TAG, "Exception while copying appops xml", exc);
+            fail();
+        }
+    }
+
+    private void assertSameModes(SparseArray<AppOpsService.UidState> uidStates, int op1, int op2) {
+        int numberOfNonDefaultOps = 0;
+        final int defaultModeOp1 = AppOpsManager.opToDefaultMode(op1);
+        final int defaultModeOp2 = AppOpsManager.opToDefaultMode(op2);
+        for(int i = 0; i < uidStates.size(); i++) {
+            final AppOpsService.UidState uidState = uidStates.valueAt(i);
+            if (uidState.opModes != null) {
+                final int uidMode1 = uidState.opModes.get(op1, defaultModeOp1);
+                final int uidMode2 = uidState.opModes.get(op2, defaultModeOp2);
+                assertEquals(uidMode1, uidMode2);
+                if (uidMode1 != defaultModeOp1) {
+                    numberOfNonDefaultOps++;
+                }
+            }
+            if (uidState.pkgOps == null) {
+                continue;
+            }
+            for (int j = 0; j < uidState.pkgOps.size(); j++) {
+                final AppOpsService.Ops ops = uidState.pkgOps.valueAt(j);
+                if (ops == null) {
+                    continue;
+                }
+                final AppOpsService.Op _op1 = ops.get(op1);
+                final AppOpsService.Op _op2 = ops.get(op2);
+                final int mode1 = (_op1 == null) ? defaultModeOp1 : _op1.mode;
+                final int mode2 = (_op2 == null) ? defaultModeOp2 : _op2.mode;
+                assertEquals(mode1, mode2);
+                if (mode1 != defaultModeOp1) {
+                    numberOfNonDefaultOps++;
+                }
+            }
+        }
+        assertEquals(numberOfNonDefaultOps, NON_DEFAULT_OPS_IN_FILE);
+    }
+
+    @Before
+    public void setUp() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mAppOpsFile = new File(mContext.getFilesDir(), APP_OPS_FILENAME);
+        extractAppOpsFile();
+        HandlerThread handlerThread = new HandlerThread(TAG);
+        handlerThread.start();
+        mHandler = new Handler(handlerThread.getLooper());
+    }
+
+    @Test
+    public void testUpgradeFromNoVersion() throws Exception {
+        AppOpsDataParser parser = new AppOpsDataParser(mAppOpsFile);
+        assertTrue(parser.parse());
+        assertEquals(AppOpsDataParser.NO_VERSION, parser.mVersion);
+        AppOpsService testService = new AppOpsService(mAppOpsFile, mHandler); // trigger upgrade
+        assertSameModes(testService.mUidStates, AppOpsManager.OP_RUN_IN_BACKGROUND,
+                AppOpsManager.OP_RUN_ANY_IN_BACKGROUND);
+        testService.mContext = mContext;
+        mHandler.removeCallbacks(testService.mWriteRunner);
+        testService.writeState();
+        assertTrue(parser.parse());
+        assertEquals(CURRENT_VERSION, parser.mVersion);
+    }
+
+    /**
+     * Class to parse data from the appops xml. Currently only parses and holds the version number.
+     * Other fields may be added as and when required for testing.
+     */
+    private static final class AppOpsDataParser {
+        static final int NO_VERSION = -1;
+        int mVersion;
+        private File mFile;
+
+        AppOpsDataParser(File file) {
+            mFile = file;
+            mVersion = NO_VERSION;
+        }
+
+        boolean parse() {
+            try (FileInputStream stream = new FileInputStream(mFile)) {
+                XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(stream, StandardCharsets.UTF_8.name());
+                int type;
+                while ((type = parser.next()) != XmlPullParser.START_TAG
+                        && type != XmlPullParser.END_DOCUMENT) {
+                    ;
+                }
+                if (type != XmlPullParser.START_TAG) {
+                    throw new IllegalStateException("no start tag found");
+                }
+                final String versionString = parser.getAttributeValue(null, "v");
+                if (versionString != null) {
+                    mVersion = Integer.parseInt(versionString);
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "Failed while parsing test appops xml", e);
+                return false;
+            }
+            return true;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
index ceb92de..82ff0d8 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java
@@ -31,6 +31,7 @@
 import static org.mockito.Mockito.when;
 
 import android.Manifest.permission;
+import android.app.AppOpsManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -61,15 +62,18 @@
 
 @RunWith(AndroidJUnit4.class)
 public class NetworkScorerAppManagerTest {
+    private static final int PACKAGE_UID = 924;
     private static String MOCK_SERVICE_LABEL = "Mock Service";
     private static String MOCK_OVERRIDEN_SERVICE_LABEL = "Mock Service Label Override";
     private static String MOCK_NETWORK_AVAILABLE_NOTIFICATION_CHANNEL_ID =
             "Mock Network Available Notification Channel Id";
+    private static final ComponentName RECO_COMPONENT = new ComponentName("package1", "class1");
 
     @Mock private Context mMockContext;
     @Mock private PackageManager mMockPm;
     @Mock private Resources mResources;
     @Mock private NetworkScorerAppManager.SettingsFacade mSettingsFacade;
+    @Mock private AppOpsManager mAppOpsManager;
     private NetworkScorerAppManager mNetworkScorerAppManager;
     private List<ResolveInfo> mAvailableServices;
 
@@ -86,45 +90,69 @@
             }
         }), eq(PackageManager.GET_META_DATA))).thenReturn(mAvailableServices);
         when(mMockContext.getResources()).thenReturn(mResources);
+        when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
+
+        mockLocationModeOn();
+        mockLocationPermissionGranted(PACKAGE_UID, RECO_COMPONENT.getPackageName());
 
         mNetworkScorerAppManager = new NetworkScorerAppManager(mMockContext, mSettingsFacade);
     }
 
     @Test
     public void testGetActiveScorer_providerAvailable() throws Exception {
-        final ComponentName recoComponent = new ComponentName("package1", "class1");
-        setNetworkRecoPackageSetting(recoComponent.getPackageName());
-        mockScoreNetworksGranted(recoComponent.getPackageName());
-        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */);
+        setNetworkRecoPackageSetting(RECO_COMPONENT.getPackageName());
+        mockScoreNetworksGranted(RECO_COMPONENT.getPackageName());
+        mockRecommendationServiceAvailable(RECO_COMPONENT, PACKAGE_UID /* packageUid */);
 
         final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
         assertNotNull(activeScorer);
-        assertEquals(recoComponent, activeScorer.getRecommendationServiceComponent());
-        assertEquals(924, activeScorer.packageUid);
+        assertEquals(RECO_COMPONENT, activeScorer.getRecommendationServiceComponent());
+        assertEquals(PACKAGE_UID, activeScorer.packageUid);
         assertEquals(MOCK_SERVICE_LABEL, activeScorer.getRecommendationServiceLabel());
     }
 
     @Test
     public void testGetActiveScorer_providerAvailable_serviceLabelOverride() throws Exception {
-        final ComponentName recoComponent = new ComponentName("package1", "class1");
-        setNetworkRecoPackageSetting(recoComponent.getPackageName());
-        mockScoreNetworksGranted(recoComponent.getPackageName());
-        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */,
+        setNetworkRecoPackageSetting(RECO_COMPONENT.getPackageName());
+        mockScoreNetworksGranted(RECO_COMPONENT.getPackageName());
+        mockRecommendationServiceAvailable(RECO_COMPONENT, PACKAGE_UID /* packageUid */,
                 null /* enableUseOpenWifiPackageActivityPackage*/, true /* serviceLabelOverride */);
 
         final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
         assertNotNull(activeScorer);
-        assertEquals(recoComponent, activeScorer.getRecommendationServiceComponent());
-        assertEquals(924, activeScorer.packageUid);
+        assertEquals(RECO_COMPONENT, activeScorer.getRecommendationServiceComponent());
+        assertEquals(PACKAGE_UID, activeScorer.packageUid);
         assertEquals(MOCK_OVERRIDEN_SERVICE_LABEL, activeScorer.getRecommendationServiceLabel());
     }
 
     @Test
-    public void testGetActiveScorer_permissionMissing() throws Exception {
-        final ComponentName recoComponent = new ComponentName("package1", "class1");
-        setNetworkRecoPackageSetting(recoComponent.getPackageName());
-        mockScoreNetworksDenied(recoComponent.getPackageName());
-        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */);
+    public void testGetActiveScorer_scoreNetworksPermissionMissing() throws Exception {
+        setNetworkRecoPackageSetting(RECO_COMPONENT.getPackageName());
+        mockScoreNetworksDenied(RECO_COMPONENT.getPackageName());
+        mockRecommendationServiceAvailable(RECO_COMPONENT, PACKAGE_UID /* packageUid */);
+
+        final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
+        assertNull(activeScorer);
+    }
+
+    @Test
+    public void testGetActiveScorer_locationPermissionMissing() throws Exception {
+        setNetworkRecoPackageSetting(RECO_COMPONENT.getPackageName());
+        mockScoreNetworksGranted(RECO_COMPONENT.getPackageName());
+        mockLocationPermissionDenied(PACKAGE_UID, RECO_COMPONENT.getPackageName());
+        mockRecommendationServiceAvailable(RECO_COMPONENT, PACKAGE_UID /* packageUid */);
+
+        final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
+        assertNull(activeScorer);
+    }
+
+    @Test
+    public void testGetActiveScorer_locationModeOff() throws Exception {
+        setNetworkRecoPackageSetting(RECO_COMPONENT.getPackageName());
+        mockScoreNetworksGranted(RECO_COMPONENT.getPackageName());
+        mockLocationPermissionGranted(PACKAGE_UID, RECO_COMPONENT.getPackageName());
+        mockLocationModeOff();
+        mockRecommendationServiceAvailable(RECO_COMPONENT, PACKAGE_UID /* packageUid */);
 
         final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
         assertNull(activeScorer);
@@ -133,67 +161,63 @@
     @Test
     public void testGetActiveScorer_providerAvailable_enableUseOpenWifiActivityNotSet()
             throws Exception {
-        final ComponentName recoComponent = new ComponentName("package1", "class1");
-        setNetworkRecoPackageSetting(recoComponent.getPackageName());
-        mockScoreNetworksGranted(recoComponent.getPackageName());
-        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */,
+        setNetworkRecoPackageSetting(RECO_COMPONENT.getPackageName());
+        mockScoreNetworksGranted(RECO_COMPONENT.getPackageName());
+        mockRecommendationServiceAvailable(RECO_COMPONENT, PACKAGE_UID /* packageUid */,
                 null /* enableUseOpenWifiPackageActivityPackage*/);
 
         final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
         assertNotNull(activeScorer);
-        assertEquals(recoComponent, activeScorer.getRecommendationServiceComponent());
-        assertEquals(924, activeScorer.packageUid);
+        assertEquals(RECO_COMPONENT, activeScorer.getRecommendationServiceComponent());
+        assertEquals(PACKAGE_UID, activeScorer.packageUid);
         assertNull(activeScorer.getEnableUseOpenWifiActivity());
     }
 
     @Test
     public void testGetActiveScorer_providerAvailable_enableUseOpenWifiActivityNotResolved()
             throws Exception {
-        final ComponentName recoComponent = new ComponentName("package1", "class1");
-        setNetworkRecoPackageSetting(recoComponent.getPackageName());
-        mockScoreNetworksGranted(recoComponent.getPackageName());
-        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */,
+        setNetworkRecoPackageSetting(RECO_COMPONENT.getPackageName());
+        mockScoreNetworksGranted(RECO_COMPONENT.getPackageName());
+        mockRecommendationServiceAvailable(RECO_COMPONENT, PACKAGE_UID /* packageUid */,
                 "package2" /* enableUseOpenWifiPackageActivityPackage*/);
 
         final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
         assertNotNull(activeScorer);
-        assertEquals(recoComponent, activeScorer.getRecommendationServiceComponent());
-        assertEquals(924, activeScorer.packageUid);
+        assertEquals(RECO_COMPONENT, activeScorer.getRecommendationServiceComponent());
+        assertEquals(PACKAGE_UID, activeScorer.packageUid);
         assertNull(activeScorer.getEnableUseOpenWifiActivity());
     }
 
     @Test
     public void testGetActiveScorer_providerAvailable_enableUseOpenWifiActivityResolved()
             throws Exception {
-        final ComponentName recoComponent = new ComponentName("package1", "class1");
         final ComponentName enableUseOpenWifiComponent = new ComponentName("package2", "class2");
-        setNetworkRecoPackageSetting(recoComponent.getPackageName());
-        mockScoreNetworksGranted(recoComponent.getPackageName());
-        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */,
+        setNetworkRecoPackageSetting(RECO_COMPONENT.getPackageName());
+        mockScoreNetworksGranted(RECO_COMPONENT.getPackageName());
+        mockRecommendationServiceAvailable(RECO_COMPONENT, PACKAGE_UID /* packageUid */,
                 enableUseOpenWifiComponent.getPackageName());
         mockEnableUseOpenWifiActivity(enableUseOpenWifiComponent);
 
         final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
         assertNotNull(activeScorer);
-        assertEquals(recoComponent, activeScorer.getRecommendationServiceComponent());
-        assertEquals(924, activeScorer.packageUid);
+        assertEquals(RECO_COMPONENT, activeScorer.getRecommendationServiceComponent());
+        assertEquals(PACKAGE_UID, activeScorer.packageUid);
         assertEquals(enableUseOpenWifiComponent, activeScorer.getEnableUseOpenWifiActivity());
         assertNull(activeScorer.getNetworkAvailableNotificationChannelId());
     }
 
     @Test
     public void testGetActiveScorer_providerAvailable_networkAvailableNotificationChannelIdSet() {
-        final ComponentName recoComponent = new ComponentName("package1", "class1");
-        setNetworkRecoPackageSetting(recoComponent.getPackageName());
-        mockScoreNetworksGranted(recoComponent.getPackageName());
-        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */,
+        setNetworkRecoPackageSetting(RECO_COMPONENT.getPackageName());
+        mockScoreNetworksGranted(RECO_COMPONENT.getPackageName());
+        mockRecommendationServiceAvailable(RECO_COMPONENT, PACKAGE_UID /* packageUid */,
                 null /* enableUseOpenWifiActivityPackage */, false /* serviceLabelOverride */,
                 true /* setNotificationChannelId */);
 
         final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
         assertNotNull(activeScorer);
-        assertEquals(recoComponent, activeScorer.getRecommendationServiceComponent());
-        assertEquals(924, activeScorer.packageUid);
+        assertEquals(RECO_COMPONENT, activeScorer.getRecommendationServiceComponent());
+        assertEquals(PACKAGE_UID, activeScorer.packageUid);
         assertEquals(MOCK_NETWORK_AVAILABLE_NOTIFICATION_CHANNEL_ID,
                 activeScorer.getNetworkAvailableNotificationChannelId());
     }
@@ -209,9 +233,8 @@
 
     @Test
     public void testGetActiveScorer_packageSettingIsInvalid() throws Exception {
-        final ComponentName recoComponent = new ComponentName("package1", "class1");
-        setDefaultNetworkRecommendationPackage(recoComponent.getPackageName());
-        mockScoreNetworksGranted(recoComponent.getPackageName());
+        setDefaultNetworkRecommendationPackage(RECO_COMPONENT.getPackageName());
+        mockScoreNetworksGranted(RECO_COMPONENT.getPackageName());
         // NETWORK_RECOMMENDATIONS_PACKAGE is set to a package that isn't a recommender.
 
         final NetworkScorerAppData activeScorer = mNetworkScorerAppManager.getActiveScorer();
@@ -249,12 +272,12 @@
 
     @Test
     public void testSetActiveScorer_validPackage() throws Exception {
-        String packageName = "package";
         String newPackage = "newPackage";
-        setNetworkRecoPackageSetting(packageName);
+        int newAppUid = 621;
         final ComponentName recoComponent = new ComponentName(newPackage, "class1");
         mockScoreNetworksGranted(recoComponent.getPackageName());
-        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */, null);
+        mockRecommendationServiceAvailable(recoComponent, newAppUid, null);
+        mockLocationPermissionGranted(newAppUid, recoComponent.getPackageName());
 
         assertTrue(mNetworkScorerAppManager.setActiveScorer(newPackage));
         verify(mSettingsFacade).putString(mMockContext,
@@ -289,11 +312,9 @@
 
     @Test
     public void testUpdateState_currentPackageValid() throws Exception {
-        String packageName = "package";
-        setNetworkRecoPackageSetting(packageName);
-        final ComponentName recoComponent = new ComponentName(packageName, "class1");
-        mockScoreNetworksGranted(recoComponent.getPackageName());
-        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */, null);
+        setNetworkRecoPackageSetting(RECO_COMPONENT.getPackageName());
+        mockScoreNetworksGranted(RECO_COMPONENT.getPackageName());
+        mockRecommendationServiceAvailable(RECO_COMPONENT, PACKAGE_UID , null);
 
         mNetworkScorerAppManager.updateState();
 
@@ -306,11 +327,13 @@
 
     @Test
     public void testUpdateState_currentPackageNotValid_validDefault() throws Exception {
-        String defaultPackage = "defaultPackage";
-        setDefaultNetworkRecommendationPackage(defaultPackage);
+        final String defaultPackage = "defaultPackage";
+        final int defaultAppUid = 621;
         final ComponentName recoComponent = new ComponentName(defaultPackage, "class1");
+        setDefaultNetworkRecommendationPackage(defaultPackage);
         mockScoreNetworksGranted(recoComponent.getPackageName());
-        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */, null);
+        mockRecommendationServiceAvailable(recoComponent, defaultAppUid, null);
+        mockLocationPermissionGranted(defaultAppUid, defaultPackage);
 
         mNetworkScorerAppManager.updateState();
 
@@ -329,8 +352,6 @@
 
         mNetworkScorerAppManager.updateState();
 
-        verify(mSettingsFacade).putString(mMockContext,
-                Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE, defaultPackage);
         verify(mSettingsFacade).putInt(mMockContext,
                 Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED,
                 NetworkScoreManager.RECOMMENDATIONS_ENABLED_OFF);
@@ -345,8 +366,9 @@
 
         verify(mSettingsFacade, never()).putString(any(),
                 eq(Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE), anyString());
-        verify(mSettingsFacade, never()).putInt(any(),
-                eq(Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED), anyInt());
+        verify(mSettingsFacade).putInt(mMockContext,
+                Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED,
+                NetworkScoreManager.RECOMMENDATIONS_ENABLED_OFF);
     }
 
     @Test
@@ -358,8 +380,9 @@
 
         verify(mSettingsFacade, never()).putString(any(),
                 eq(Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE), anyString());
-        verify(mSettingsFacade, never()).putInt(any(),
-                eq(Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED), anyInt());
+        verify(mSettingsFacade).putInt(mMockContext,
+                Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED,
+                NetworkScoreManager.RECOMMENDATIONS_ENABLED_OFF);
     }
 
     @Test
@@ -370,8 +393,9 @@
 
         mNetworkScorerAppManager.updateState();
 
-        verify(mSettingsFacade, never()).putString(any(),
-                eq(Settings.Global.NETWORK_RECOMMENDATIONS_PACKAGE), any());
+        verify(mSettingsFacade).putInt(mMockContext,
+                Settings.Global.NETWORK_RECOMMENDATIONS_ENABLED,
+                NetworkScoreManager.RECOMMENDATIONS_ENABLED_OFF);
     }
 
     @Test
@@ -415,11 +439,10 @@
     @Test
     public void testMigrateNetworkScorerAppSettingIfNeeded_useOpenWifiSettingIsNotEmpty()
             throws Exception {
-        final ComponentName recoComponent = new ComponentName("package1", "class1");
         final ComponentName enableUseOpenWifiComponent = new ComponentName("package2", "class2");
-        setNetworkRecoPackageSetting(recoComponent.getPackageName());
-        mockScoreNetworksGranted(recoComponent.getPackageName());
-        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */,
+        setNetworkRecoPackageSetting(RECO_COMPONENT.getPackageName());
+        mockScoreNetworksGranted(RECO_COMPONENT.getPackageName());
+        mockRecommendationServiceAvailable(RECO_COMPONENT, PACKAGE_UID /* packageUid */,
                 enableUseOpenWifiComponent.getPackageName());
         mockEnableUseOpenWifiActivity(enableUseOpenWifiComponent);
         when(mSettingsFacade.getString(mMockContext,
@@ -441,13 +464,12 @@
     @Test
     public void testMigrateNetworkScorerAppSettingIfNeeded_useOpenWifiActivityNotAvail()
             throws Exception {
-        final ComponentName recoComponent = new ComponentName("package1", "class1");
         final ComponentName enableUseOpenWifiComponent = new ComponentName("package2", "class2");
-        setNetworkRecoPackageSetting(recoComponent.getPackageName());
-        mockScoreNetworksGranted(recoComponent.getPackageName());
+        setNetworkRecoPackageSetting(RECO_COMPONENT.getPackageName());
+        mockScoreNetworksGranted(RECO_COMPONENT.getPackageName());
         // The active component doesn't have an open wifi activity so the migration shouldn't
         // set USE_OPEN_WIFI_PACKAGE.
-        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */,
+        mockRecommendationServiceAvailable(RECO_COMPONENT, PACKAGE_UID /* packageUid */,
                 null /*useOpenWifiActivityPackage*/);
         when(mSettingsFacade.getString(mMockContext,
                 Settings.Global.NETWORK_SCORER_APP))
@@ -466,11 +488,10 @@
     @Test
     public void testMigrateNetworkScorerAppSettingIfNeeded_packageMismatch_activity()
             throws Exception {
-        final ComponentName recoComponent = new ComponentName("package1", "class1");
         final ComponentName enableUseOpenWifiComponent = new ComponentName("package2", "class2");
-        setNetworkRecoPackageSetting(recoComponent.getPackageName());
-        mockScoreNetworksGranted(recoComponent.getPackageName());
-        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */,
+        setNetworkRecoPackageSetting(RECO_COMPONENT.getPackageName());
+        mockScoreNetworksGranted(RECO_COMPONENT.getPackageName());
+        mockRecommendationServiceAvailable(RECO_COMPONENT, PACKAGE_UID /* packageUid */,
                 enableUseOpenWifiComponent.getPackageName());
         mockEnableUseOpenWifiActivity(enableUseOpenWifiComponent);
         // The older network scorer app setting doesn't match the new use open wifi activity package
@@ -492,18 +513,17 @@
     @Test
     public void testMigrateNetworkScorerAppSettingIfNeeded_packageMismatch_service()
             throws Exception {
-        final ComponentName recoComponent = new ComponentName("package1", "class1");
         final ComponentName enableUseOpenWifiComponent = new ComponentName("package2", "class2");
-        setNetworkRecoPackageSetting(recoComponent.getPackageName());
-        mockScoreNetworksGranted(recoComponent.getPackageName());
-        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */,
+        setNetworkRecoPackageSetting(RECO_COMPONENT.getPackageName());
+        mockScoreNetworksGranted(RECO_COMPONENT.getPackageName());
+        mockRecommendationServiceAvailable(RECO_COMPONENT, PACKAGE_UID /* packageUid */,
                 enableUseOpenWifiComponent.getPackageName());
         mockEnableUseOpenWifiActivity(enableUseOpenWifiComponent);
         // The older network scorer app setting doesn't match the active package so the migration
         // shouldn't set USE_OPEN_WIFI_PACKAGE.
         when(mSettingsFacade.getString(mMockContext,
                 Settings.Global.NETWORK_SCORER_APP))
-                .thenReturn(recoComponent.getPackageName() + ".diff");
+                .thenReturn(RECO_COMPONENT.getPackageName() + ".diff");
         when(mSettingsFacade.getString(mMockContext,
                 Settings.Global.USE_OPEN_WIFI_PACKAGE)).thenReturn(null);
 
@@ -518,11 +538,10 @@
     @Test
     public void testMigrateNetworkScorerAppSettingIfNeeded_packageMatch_activity()
             throws Exception {
-        final ComponentName recoComponent = new ComponentName("package1", "class1");
         final ComponentName enableUseOpenWifiComponent = new ComponentName("package2", "class2");
-        setNetworkRecoPackageSetting(recoComponent.getPackageName());
-        mockScoreNetworksGranted(recoComponent.getPackageName());
-        mockRecommendationServiceAvailable(recoComponent, 924 /* packageUid */,
+        setNetworkRecoPackageSetting(RECO_COMPONENT.getPackageName());
+        mockScoreNetworksGranted(RECO_COMPONENT.getPackageName());
+        mockRecommendationServiceAvailable(RECO_COMPONENT, PACKAGE_UID /* packageUid */,
                 enableUseOpenWifiComponent.getPackageName());
         mockEnableUseOpenWifiActivity(enableUseOpenWifiComponent);
         // Old setting matches the new activity package, migration should happen.
@@ -566,6 +585,33 @@
                 .thenReturn(PackageManager.PERMISSION_DENIED);
     }
 
+    private void mockLocationModeOn() {
+        mockLocationModeValue(Settings.Secure.LOCATION_MODE_HIGH_ACCURACY);
+    }
+
+    private void mockLocationModeOff() {
+        mockLocationModeValue(Settings.Secure.LOCATION_MODE_OFF);
+    }
+
+    private void mockLocationModeValue(int returnVal) {
+        when(mSettingsFacade.getSecureInt(eq(mMockContext),
+                eq(Settings.Secure.LOCATION_MODE), anyInt())).thenReturn(returnVal);
+    }
+
+    private void mockLocationPermissionGranted(int uid, String packageName) {
+        when(mMockPm.checkPermission(permission.ACCESS_COARSE_LOCATION, packageName))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+        when(mAppOpsManager.noteOp(AppOpsManager.OP_COARSE_LOCATION, uid, packageName))
+                .thenReturn(AppOpsManager.MODE_ALLOWED);
+    }
+
+    private void mockLocationPermissionDenied(int uid, String packageName) {
+        when(mMockPm.checkPermission(permission.ACCESS_COARSE_LOCATION, packageName))
+                .thenReturn(PackageManager.PERMISSION_DENIED);
+        when(mAppOpsManager.noteOp(AppOpsManager.OP_COARSE_LOCATION, uid, packageName))
+                .thenReturn(AppOpsManager.MODE_IGNORED);
+    }
+
     private void mockRecommendationServiceAvailable(final ComponentName compName, int packageUid) {
         mockRecommendationServiceAvailable(compName, packageUid, null, false);
     }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
new file mode 100644
index 0000000..194f4ae
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStartInterceptorTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
+import static org.mockito.Mockito.when;
+
+import android.app.KeyguardManager;
+import android.app.admin.DevicePolicyManagerInternal;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.UserInfo;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.support.test.filters.SmallTest;
+
+import com.android.internal.app.UnlaunchableAppActivity;
+import com.android.server.LocalServices;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit tests for {@link ActivityStartInterceptorTest}.
+ *
+ * Build/Install/Run:
+ *  bit FrameworksServicesTests:com.android.server.am.ActivityStartInterceptorTest
+ */
+@SmallTest
+public class ActivityStartInterceptorTest {
+    private static final int TEST_USER_ID = 1;
+    private static final int TEST_REAL_CALLING_UID = 2;
+    private static final int TEST_REAL_CALLING_PID = 3;
+    private static final String TEST_CALLING_PACKAGE = "com.test.caller";
+    private static final int TEST_START_FLAGS = 4;
+    private static final Intent ADMIN_SUPPORT_INTENT =
+            new Intent("com.test.ADMIN_SUPPORT");
+    private static final Intent CONFIRM_CREDENTIALS_INTENT =
+            new Intent("com.test.CONFIRM_CREDENTIALS");
+    private static final UserInfo PARENT_USER_INFO = new UserInfo(0 /* userId */, "parent",
+            0 /* flags */);
+    private static final String TEST_PACKAGE_NAME = "com.test.package";
+
+    @Mock
+    private Context mContext;
+    @Mock
+    private ActivityManagerService mService;
+    @Mock
+    private ActivityStackSupervisor mSupervisor;
+    @Mock
+    private DevicePolicyManagerInternal mDevicePolicyManager;
+    @Mock
+    private UserManager mUserManager;
+    @Mock
+    private UserController mUserController;
+    @Mock
+    private KeyguardManager mKeyguardManager;
+
+    private ActivityStartInterceptor mInterceptor;
+    private ActivityInfo mAInfo = new ActivityInfo();
+
+    @Before
+    public void setUp() {
+        // This property is used to allow mocking of package private classes with mockito
+        System.setProperty("dexmaker.share_classloader", "true");
+
+        MockitoAnnotations.initMocks(this);
+        mInterceptor = new ActivityStartInterceptor(mService, mSupervisor, mContext,
+                mUserController);
+        mInterceptor.setStates(TEST_USER_ID, TEST_REAL_CALLING_PID, TEST_REAL_CALLING_UID,
+                TEST_START_FLAGS, TEST_CALLING_PACKAGE);
+
+        // Mock DevicePolicyManagerInternal
+        LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
+        LocalServices.addService(DevicePolicyManagerInternal.class,
+                mDevicePolicyManager);
+        when(mDevicePolicyManager
+                        .createShowAdminSupportIntent(TEST_USER_ID, true))
+                .thenReturn(ADMIN_SUPPORT_INTENT);
+
+        // Mock UserManager
+        when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+        when(mUserManager.getProfileParent(TEST_USER_ID)).thenReturn(PARENT_USER_INFO);
+
+        // Mock KeyguardManager
+        when(mContext.getSystemService(Context.KEYGUARD_SERVICE)).thenReturn(mKeyguardManager);
+        when(mKeyguardManager.createConfirmDeviceCredentialIntent(
+                nullable(CharSequence.class), nullable(CharSequence.class), eq(TEST_USER_ID))).
+                thenReturn(CONFIRM_CREDENTIALS_INTENT);
+
+        // Initialise activity info
+        mAInfo.packageName = TEST_PACKAGE_NAME;
+        mAInfo.applicationInfo = new ApplicationInfo();
+    }
+
+    @Test
+    public void testSuspendedPackage() {
+        // GIVEN the package we're about to launch is currently suspended
+        mAInfo.applicationInfo.flags = FLAG_SUSPENDED;
+
+        // THEN calling intercept returns true
+        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+
+        // THEN the returned intent is the admin support intent
+        assertEquals(ADMIN_SUPPORT_INTENT, mInterceptor.mIntent);
+    }
+
+    @Test
+    public void testInterceptQuietProfile() {
+        // GIVEN that the user the activity is starting as is currently in quiet mode
+        when(mUserManager.isQuietModeEnabled(eq(UserHandle.of(TEST_USER_ID)))).thenReturn(true);
+
+        // THEN calling intercept returns true
+        assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+
+        // THEN the returned intent is the quiet mode intent
+        assertTrue(UnlaunchableAppActivity.createInQuietModeDialogIntent(TEST_USER_ID)
+                .filterEquals(mInterceptor.mIntent));
+    }
+
+    @Test
+    public void testWorkChallenge() {
+        // GIVEN that the user the activity is starting as is currently locked
+        when(mUserController.shouldConfirmCredentials(TEST_USER_ID)).thenReturn(true);
+
+        // THEN calling intercept returns true
+        mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null);
+
+        // THEN the returned intent is the quiet mode intent
+        assertTrue(CONFIRM_CREDENTIALS_INTENT.filterEquals(mInterceptor.mIntent));
+    }
+
+    @Test
+    public void testNoInterception() {
+        // GIVEN that none of the interception conditions are met
+
+        // THEN calling intercept returns false
+        assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/AppsBackedUpOnThisDeviceJournalTest.java b/services/tests/servicestests/src/com/android/server/backup/AppsBackedUpOnThisDeviceJournalTest.java
new file mode 100644
index 0000000..093f920
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/backup/AppsBackedUpOnThisDeviceJournalTest.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2017 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.backup;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.google.android.collect.Sets;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.util.HashSet;
+import java.util.Set;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class AppsBackedUpOnThisDeviceJournalTest {
+    private static final String JOURNAL_FILE_NAME = "processed";
+
+    private static final String GOOGLE_PHOTOS = "com.google.photos";
+    private static final String GMAIL = "com.google.gmail";
+    private static final String GOOGLE_PLUS = "com.google.plus";
+
+    @Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
+
+    private File mStateDirectory;
+    private AppsBackedUpOnThisDeviceJournal mAppsBackedUpOnThisDeviceJournal;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        mStateDirectory = mTemporaryFolder.newFolder();
+        mAppsBackedUpOnThisDeviceJournal = new AppsBackedUpOnThisDeviceJournal(mStateDirectory);
+    }
+
+    @Test
+    public void constructor_loadsAnyPreviousJournalFromDisk() throws Exception {
+        writePermanentJournalPackages(Sets.newHashSet(GOOGLE_PHOTOS, GMAIL));
+
+        AppsBackedUpOnThisDeviceJournal journalFromDisk =
+                new AppsBackedUpOnThisDeviceJournal(mStateDirectory);
+
+        assertThat(journalFromDisk.hasBeenProcessed(GOOGLE_PHOTOS)).isTrue();
+        assertThat(journalFromDisk.hasBeenProcessed(GMAIL)).isTrue();
+    }
+
+    @Test
+    public void hasBeenProcessed_isFalseForAnyPackageFromBlankInit() {
+        assertThat(mAppsBackedUpOnThisDeviceJournal.hasBeenProcessed(GOOGLE_PHOTOS)).isFalse();
+        assertThat(mAppsBackedUpOnThisDeviceJournal.hasBeenProcessed(GMAIL)).isFalse();
+        assertThat(mAppsBackedUpOnThisDeviceJournal.hasBeenProcessed(GOOGLE_PLUS)).isFalse();
+    }
+
+    @Test
+    public void addPackage_addsPackageToObjectState() {
+        mAppsBackedUpOnThisDeviceJournal.addPackage(GOOGLE_PHOTOS);
+
+        assertThat(mAppsBackedUpOnThisDeviceJournal.hasBeenProcessed(GOOGLE_PHOTOS)).isTrue();
+    }
+
+    @Test
+    public void addPackage_addsPackageToFileSystem() throws Exception {
+        mAppsBackedUpOnThisDeviceJournal.addPackage(GOOGLE_PHOTOS);
+
+        assertThat(readJournalPackages()).contains(GOOGLE_PHOTOS);
+    }
+
+    @Test
+    public void getPackagesCopy_returnsTheCurrentState() throws Exception {
+        mAppsBackedUpOnThisDeviceJournal.addPackage(GOOGLE_PHOTOS);
+        mAppsBackedUpOnThisDeviceJournal.addPackage(GMAIL);
+
+        assertThat(mAppsBackedUpOnThisDeviceJournal.getPackagesCopy())
+                .isEqualTo(Sets.newHashSet(GOOGLE_PHOTOS, GMAIL));
+    }
+
+    @Test
+    public void getPackagesCopy_returnsACopy() throws Exception {
+        mAppsBackedUpOnThisDeviceJournal.getPackagesCopy().add(GMAIL);
+
+        assertThat(mAppsBackedUpOnThisDeviceJournal.hasBeenProcessed(GMAIL)).isFalse();
+    }
+
+    @Test
+    public void reset_removesAllPackagesFromObjectState() {
+        mAppsBackedUpOnThisDeviceJournal.addPackage(GOOGLE_PHOTOS);
+        mAppsBackedUpOnThisDeviceJournal.addPackage(GOOGLE_PLUS);
+        mAppsBackedUpOnThisDeviceJournal.addPackage(GMAIL);
+
+        mAppsBackedUpOnThisDeviceJournal.reset();
+
+        assertThat(mAppsBackedUpOnThisDeviceJournal.hasBeenProcessed(GOOGLE_PHOTOS)).isFalse();
+        assertThat(mAppsBackedUpOnThisDeviceJournal.hasBeenProcessed(GMAIL)).isFalse();
+        assertThat(mAppsBackedUpOnThisDeviceJournal.hasBeenProcessed(GOOGLE_PLUS)).isFalse();
+    }
+
+    @Test
+    public void reset_removesAllPackagesFromFileSystem() throws Exception {
+        mAppsBackedUpOnThisDeviceJournal.addPackage(GOOGLE_PHOTOS);
+        mAppsBackedUpOnThisDeviceJournal.addPackage(GOOGLE_PLUS);
+        mAppsBackedUpOnThisDeviceJournal.addPackage(GMAIL);
+
+        mAppsBackedUpOnThisDeviceJournal.reset();
+
+        assertThat(readJournalPackages()).isEmpty();
+    }
+
+    private HashSet<String> readJournalPackages() throws Exception {
+        File journal = new File(mStateDirectory, JOURNAL_FILE_NAME);
+        HashSet<String> packages = new HashSet<>();
+
+        try (FileInputStream fis = new FileInputStream(journal);
+             DataInputStream dis = new DataInputStream(fis)) {
+            while (dis.available() > 0) {
+                packages.add(dis.readUTF());
+            }
+        } catch (FileNotFoundException e) {
+            return new HashSet<>();
+        }
+
+        return packages;
+    }
+
+    private void writePermanentJournalPackages(Set<String> packages) throws Exception {
+        File journal = new File(mStateDirectory, JOURNAL_FILE_NAME);
+
+        try (FileOutputStream fos = new FileOutputStream(journal);
+             DataOutputStream dos = new DataOutputStream(fos)) {
+            for (String packageName : packages) {
+                dos.writeUTF(packageName);
+            }
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
index 45b107d..0e940f2 100644
--- a/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
+++ b/services/tests/servicestests/src/com/android/server/backup/testutils/PackageManagerStub.java
@@ -275,6 +275,11 @@
     }
 
     @Override
+    public String[] getNamesForUids(int uid[]) {
+        return null;
+    }
+
+    @Override
     public int getUidForSharedUser(String sharedUserName)
             throws NameNotFoundException {
         return 0;
diff --git a/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
new file mode 100644
index 0000000..70d2274
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/job/BackgroundRestrictionsTest.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2017 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.job;
+
+import static com.android.servicestests.apps.jobtestapp.TestJobService.ACTION_JOB_STARTED;
+import static com.android.servicestests.apps.jobtestapp.TestJobService.ACTION_JOB_STOPPED;
+import static com.android.servicestests.apps.jobtestapp.TestJobService.JOB_PARAMS_EXTRA_KEY;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.app.IActivityManager;
+import android.app.job.JobParameters;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.IDeviceIdleController;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import com.android.servicestests.apps.jobtestapp.TestJobActivity;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * TODO: Also add a test for temp power whitelist
+ * Tests that background restrictions on jobs work as expected.
+ * This test requires test-apps/JobTestApp to be installed on the device.
+ * To run this test from root of checkout:
+ * <pre>
+ *  mmm -j32 frameworks/base/services/tests/servicestests/
+ *  adb install out/target/product/marlin/data/app/JobTestApp/JobTestApp.apk
+ *  adb install out/target/product/marlin/data/app/FrameworksServicesTests/FrameworksServicesTests.apk
+ *  adb  shell am instrument -e class 'com.android.server.job.BackgroundRestrictionsTest' -w \
+ *  'com.android.frameworks.servicestests/android.support.test.runner.AndroidJUnitRunner'
+ * </pre>
+ */
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class BackgroundRestrictionsTest {
+    private static final String TAG = BackgroundRestrictionsTest.class.getSimpleName();
+    private static final String TEST_APP_PACKAGE = "com.android.servicestests.apps.jobtestapp";
+    private static final String TEST_APP_ACTIVITY = TEST_APP_PACKAGE + ".TestJobActivity";
+    private static final long POLL_INTERVAL = 2000;
+    private static final long DEFAULT_WAIT_TIMEOUT = 5000;
+
+    private Context mContext;
+    private AppOpsManager mAppOpsManager;
+    private IDeviceIdleController mDeviceIdleController;
+    private IActivityManager mIActivityManager;
+    private int mTestJobId;
+    private int mTestPackageUid;
+    /* accesses must be synchronized on itself */
+    private final TestJobStatus mTestJobStatus = new TestJobStatus();
+    private final BroadcastReceiver mJobStateChangeReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final JobParameters params = intent.getParcelableExtra(JOB_PARAMS_EXTRA_KEY);
+            Log.d(TAG, "Received action " + intent.getAction());
+            synchronized (mTestJobStatus) {
+                switch (intent.getAction()) {
+                    case ACTION_JOB_STARTED:
+                        mTestJobStatus.running = true;
+                        mTestJobStatus.jobId = params.getJobId();
+                        mTestJobStatus.stopReason = JobParameters.REASON_CANCELED;
+                        break;
+                    case ACTION_JOB_STOPPED:
+                        mTestJobStatus.running = false;
+                        mTestJobStatus.jobId = params.getJobId();
+                        mTestJobStatus.stopReason = params.getStopReason();
+                        break;
+                }
+            }
+        }
+    };
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
+        mDeviceIdleController = IDeviceIdleController.Stub.asInterface(
+                ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
+        mIActivityManager = ActivityManager.getService();
+        mTestPackageUid = mContext.getPackageManager().getPackageUid(TEST_APP_PACKAGE, 0);
+        mTestJobId = (int) (SystemClock.uptimeMillis() / 1000);
+        mTestJobStatus.reset();
+        final IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(ACTION_JOB_STARTED);
+        intentFilter.addAction(ACTION_JOB_STOPPED);
+        mContext.registerReceiver(mJobStateChangeReceiver, intentFilter);
+        setGlobalSwitch(true);
+        setAppOpsModeAllowed(true);
+        setPowerWhiteListed(false);
+    }
+
+    private void scheduleAndAssertJobStarted() throws Exception {
+        final Intent scheduleJobIntent = new Intent(TestJobActivity.ACTION_START_JOB);
+        scheduleJobIntent.putExtra(TestJobActivity.EXTRA_JOB_ID_KEY, mTestJobId);
+        scheduleJobIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        scheduleJobIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY));
+        mContext.startActivity(scheduleJobIntent);
+        Thread.sleep(TestJobActivity.JOB_MINIMUM_LATENCY);
+        assertTrue("Job did not start after scheduling", awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+    }
+
+    @Test
+    public void testGlobalSwitch() throws Exception {
+        setGlobalSwitch(false); // Job should not stop now.
+        scheduleAndAssertJobStarted();
+        setAppOpsModeAllowed(false);
+        mIActivityManager.makePackageIdle(TEST_APP_PACKAGE, UserHandle.USER_CURRENT);
+        assertFalse("Job stopped even when feature switch is off",
+                awaitJobStop(DEFAULT_WAIT_TIMEOUT));
+    }
+
+    @Test
+    public void testPowerWhiteList() throws Exception {
+        scheduleAndAssertJobStarted();
+        setAppOpsModeAllowed(false);
+        mIActivityManager.makePackageIdle(TEST_APP_PACKAGE, UserHandle.USER_CURRENT);
+        assertTrue("Job did not stop after making idle", awaitJobStop(DEFAULT_WAIT_TIMEOUT));
+        setPowerWhiteListed(true);
+        Thread.sleep(TestJobActivity.JOB_INITIAL_BACKOFF);
+        assertTrue("Job did not start after adding to power whitelist",
+                awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+        setPowerWhiteListed(false);
+        assertTrue("Job did not stop after removing from power whitelist",
+                awaitJobStop(DEFAULT_WAIT_TIMEOUT));
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        Intent cancelJobsIntent = new Intent(TestJobActivity.ACTION_CANCEL_JOBS);
+        cancelJobsIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_ACTIVITY));
+        cancelJobsIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        mContext.startActivity(cancelJobsIntent);
+        mContext.unregisterReceiver(mJobStateChangeReceiver);
+        setGlobalSwitch(false);
+        setAppOpsModeAllowed(true);
+        setPowerWhiteListed(false);
+    }
+
+    private void setGlobalSwitch(boolean enabled) {
+        Settings.Global.putString(mContext.getContentResolver(),
+                Settings.Global.JOB_SCHEDULER_CONSTANTS, "bg_jobs_restricted=" + enabled);
+    }
+
+    private void setPowerWhiteListed(boolean whitelist) throws RemoteException {
+        if (whitelist) {
+            mDeviceIdleController.addPowerSaveWhitelistApp(TEST_APP_PACKAGE);
+        } else {
+            mDeviceIdleController.removePowerSaveWhitelistApp(TEST_APP_PACKAGE);
+        }
+    }
+
+    private void setAppOpsModeAllowed(boolean allow) throws PackageManager.NameNotFoundException {
+        mAppOpsManager.setMode(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, mTestPackageUid,
+                TEST_APP_PACKAGE, allow ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
+    }
+
+    private boolean awaitJobStart(long timeout) throws InterruptedException {
+        return waitUntilTrue(timeout, () -> {
+            synchronized (mTestJobStatus) {
+                return (mTestJobStatus.jobId == mTestJobId) && mTestJobStatus.running;
+            }
+        });
+    }
+
+    private boolean awaitJobStop(long timeout) throws InterruptedException {
+        return waitUntilTrue(timeout, () -> {
+            synchronized (mTestJobStatus) {
+                return (mTestJobStatus.jobId == mTestJobId) && !mTestJobStatus.running &&
+                        mTestJobStatus.stopReason == JobParameters.REASON_CONSTRAINTS_NOT_SATISFIED;
+            }
+        });
+    }
+
+    private boolean waitUntilTrue(long timeout, Condition condition) throws InterruptedException {
+        final long deadLine = SystemClock.uptimeMillis() + timeout;
+        do {
+            Thread.sleep(POLL_INTERVAL);
+        } while (!condition.isTrue() && SystemClock.uptimeMillis() < deadLine);
+        return condition.isTrue();
+    }
+
+    private static final class TestJobStatus {
+        int jobId;
+        int stopReason;
+        boolean running;
+        private void reset() {
+            running = false;
+            stopReason = jobId = 0;
+        }
+    }
+
+    private interface Condition {
+        boolean isTrue();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index 856e940..91eb55b 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -31,10 +31,13 @@
 import org.junit.runner.RunWith;
 
 import android.content.res.Configuration;
+import android.os.SystemClock;
 import android.platform.test.annotations.Presubmit;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.DisplayMetrics;
 import android.util.SparseIntArray;
+import android.view.MotionEvent;
 
 import java.util.Arrays;
 import java.util.LinkedList;
@@ -237,6 +240,52 @@
         assertEquals(currentConfig.fontScale, globalConfig.fontScale, 0.1 /* delta */);
     }
 
+    /**
+     * Tests tapping on a stack in different display results in window gaining focus.
+     */
+    @Test
+    public void testInputEventBringsCorrectDisplayInFocus() throws Exception {
+        DisplayContent dc0 = sWm.getDefaultDisplayContentLocked();
+        // Create a second display
+        final DisplayContent dc1 = createNewDisplay();
+
+        // Add stack with activity.
+        final TaskStack stack0 = createTaskStackOnDisplay(dc0);
+        final Task task0 = createTaskInStack(stack0, 0 /* userId */);
+        final WindowTestUtils.TestAppWindowToken token =
+                new WindowTestUtils.TestAppWindowToken(dc0);
+        task0.addChild(token, 0);
+        dc0.mTapDetector = new TaskTapPointerEventListener(sWm, dc0);
+        sWm.registerPointerEventListener(dc0.mTapDetector);
+        final TaskStack stack1 = createTaskStackOnDisplay(dc1);
+        final Task task1 = createTaskInStack(stack1, 0 /* userId */);
+        final WindowTestUtils.TestAppWindowToken token1 =
+                new WindowTestUtils.TestAppWindowToken(dc0);
+        task1.addChild(token1, 0);
+        dc1.mTapDetector = new TaskTapPointerEventListener(sWm, dc0);
+        sWm.registerPointerEventListener(dc1.mTapDetector);
+
+        // tap on primary display (by sending ACTION_DOWN followed by ACTION_UP)
+        DisplayMetrics dm0 = dc0.getDisplayMetrics();
+        dc0.mTapDetector.onPointerEvent(
+                createTapEvent(dm0.widthPixels / 2, dm0.heightPixels / 2, true));
+        dc0.mTapDetector.onPointerEvent(
+                createTapEvent(dm0.widthPixels / 2, dm0.heightPixels / 2, false));
+
+        // Check focus is on primary display.
+        assertEquals(sWm.mCurrentFocus, dc0.findFocusedWindow());
+
+        // Tap on secondary display
+        DisplayMetrics dm1 = dc1.getDisplayMetrics();
+        dc1.mTapDetector.onPointerEvent(
+                createTapEvent(dm1.widthPixels / 2, dm1.heightPixels / 2, true));
+        dc1.mTapDetector.onPointerEvent(
+                createTapEvent(dm1.widthPixels / 2, dm1.heightPixels / 2, false));
+
+        // Check focus is on secondary.
+        assertEquals(sWm.mCurrentFocus, dc1.findFocusedWindow());
+    }
+
     @Test
     @Ignore
     public void testFocusedWindowMultipleDisplays() throws Exception {
@@ -355,4 +404,18 @@
         }
         assertTrue(actualWindows.isEmpty());
     }
+
+    private MotionEvent createTapEvent(float x, float y, boolean isDownEvent) {
+        final long downTime = SystemClock.uptimeMillis();
+        final long eventTime = SystemClock.uptimeMillis() + 100;
+        final int metaState = 0;
+
+        return MotionEvent.obtain(
+                downTime,
+                eventTime,
+                isDownEvent ? MotionEvent.ACTION_DOWN : MotionEvent.ACTION_UP,
+                x,
+                y,
+                metaState);
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 828d405..95adc9c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -18,6 +18,10 @@
 
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManagerPolicy.NAV_BAR_BOTTOM;
+
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -39,6 +43,7 @@
 import android.os.RemoteException;
 import android.view.Display;
 import android.view.IWindowManager;
+import android.view.InputChannel;
 import android.view.KeyEvent;
 import android.view.WindowManager;
 import android.view.WindowManagerPolicy;
@@ -91,7 +96,14 @@
                 }).when(am).notifyKeyguardFlagsChanged(any());
             }
 
-            sWm = WindowManagerService.main(context, mock(InputManagerService.class), true, false,
+            InputManagerService ims = mock(InputManagerService.class);
+            // InputChannel is final and can't be mocked.
+            InputChannel[] input = InputChannel.openInputChannelPair(TAG_WM);
+            if (input != null && input.length > 1) {
+                doReturn(input[1]).when(ims).monitorInput(anyString());
+            }
+
+            sWm = WindowManagerService.main(context, ims, true, false,
                     false, new TestWindowManagerPolicy());
         }
         return sWm;
diff --git a/services/tests/servicestests/test-apps/Android.mk b/services/tests/servicestests/test-apps/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/services/tests/servicestests/test-apps/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/services/tests/servicestests/test-apps/JobTestApp/Android.mk b/services/tests/servicestests/test-apps/JobTestApp/Android.mk
new file mode 100644
index 0000000..7893c91
--- /dev/null
+++ b/services/tests/servicestests/test-apps/JobTestApp/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2017 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := JobTestApp
+LOCAL_DEX_PREOPT := false
+LOCAL_PROGUARD_ENABLED := disabled
+
+include $(BUILD_PACKAGE)
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/JobTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/JobTestApp/AndroidManifest.xml
new file mode 100644
index 0000000..ac35805
--- /dev/null
+++ b/services/tests/servicestests/test-apps/JobTestApp/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.servicestests.apps.jobtestapp">
+
+    <application>
+        <service android:name=".TestJobService"
+                 android:permission="android.permission.BIND_JOB_SERVICE" />
+        <activity android:name=".TestJobActivity"
+                  android:exported="true" />
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/JobTestApp/src/com/android/servicestests/apps/jobtestapp/TestJobActivity.java b/services/tests/servicestests/test-apps/JobTestApp/src/com/android/servicestests/apps/jobtestapp/TestJobActivity.java
new file mode 100644
index 0000000..94a85ee
--- /dev/null
+++ b/services/tests/servicestests/test-apps/JobTestApp/src/com/android/servicestests/apps/jobtestapp/TestJobActivity.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 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.servicestests.apps.jobtestapp;
+
+import android.app.Activity;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+public class TestJobActivity extends Activity {
+    private static final String TAG = TestJobActivity.class.getSimpleName();
+    public static final String EXTRA_JOB_ID_KEY =
+            "com.android.servicestests.apps.jobtestapp.extra.JOB_ID";
+    public static final String ACTION_START_JOB =
+            "com.android.servicestests.apps.jobtestapp.extra.START_JOB";
+    public static final String ACTION_CANCEL_JOBS =
+            "com.android.servicestests.apps.jobtestapp.extra.CANCEL_JOBS";
+    public static final int JOB_INITIAL_BACKOFF = 10_000;
+    public static final int JOB_MINIMUM_LATENCY = 5_000;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        ComponentName jobServiceComponent = new ComponentName(this, TestJobService.class);
+        JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
+        final Intent intent = getIntent();
+        switch (intent.getAction()) {
+            case ACTION_CANCEL_JOBS:
+                jobScheduler.cancelAll();
+                Log.d(TAG, "Cancelled all jobs for " + getPackageName());
+                break;
+            case ACTION_START_JOB:
+                final int jobId = intent.getIntExtra(EXTRA_JOB_ID_KEY, hashCode());
+                JobInfo.Builder jobBuilder = new JobInfo.Builder(jobId, jobServiceComponent)
+                        .setBackoffCriteria(JOB_INITIAL_BACKOFF, JobInfo.BACKOFF_POLICY_LINEAR)
+                        .setMinimumLatency(JOB_MINIMUM_LATENCY);
+                final int result = jobScheduler.schedule(jobBuilder.build());
+                if (result != JobScheduler.RESULT_SUCCESS) {
+                    Log.e(TAG, "Could not schedule job " + jobId);
+                } else {
+                    Log.d(TAG, "Successfully scheduled job with id " + jobId);
+                }
+                break;
+        }
+        finish();
+    }
+}
\ No newline at end of file
diff --git a/services/tests/servicestests/test-apps/JobTestApp/src/com/android/servicestests/apps/jobtestapp/TestJobService.java b/services/tests/servicestests/test-apps/JobTestApp/src/com/android/servicestests/apps/jobtestapp/TestJobService.java
new file mode 100644
index 0000000..6bebb32
--- /dev/null
+++ b/services/tests/servicestests/test-apps/JobTestApp/src/com/android/servicestests/apps/jobtestapp/TestJobService.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 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.servicestests.apps.jobtestapp;
+
+import android.annotation.TargetApi;
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.content.Intent;
+import android.util.Log;
+
+@TargetApi(24)
+public class TestJobService extends JobService {
+    private static final String TAG = TestJobService.class.getSimpleName();
+    private static final String PACKAGE_NAME = "com.android.servicestests.apps.jobtestapp";
+    public static final String ACTION_JOB_STARTED = PACKAGE_NAME + ".action.JOB_STARTED";
+    public static final String ACTION_JOB_STOPPED = PACKAGE_NAME + ".action.JOB_STOPPED";
+    public static final String JOB_PARAMS_EXTRA_KEY = PACKAGE_NAME + ".extra.JOB_PARAMETERS";
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        Log.i(TAG, "Test job executing: " + params.getJobId());
+        Intent reportJobStartIntent = new Intent(ACTION_JOB_STARTED);
+        reportJobStartIntent.putExtra(JOB_PARAMS_EXTRA_KEY, params);
+        sendBroadcast(reportJobStartIntent);
+        return true;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        Log.i(TAG, "Test job stopped executing: " + params.getJobId());
+        Intent reportJobStopIntent = new Intent(ACTION_JOB_STOPPED);
+        reportJobStopIntent.putExtra(JOB_PARAMS_EXTRA_KEY, params);
+        sendBroadcast(reportJobStopIntent);
+        return true;
+    }
+}
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 177759e..db49391 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -454,10 +454,6 @@
      * @param conferenceableConnections The set of connections this connection can conference with.
      */
     public final void setConferenceableConnections(List<Connection> conferenceableConnections) {
-        if (Objects.equals(mConferenceableConnections, conferenceableConnections)) {
-            return;
-        }
-
         clearConferenceableList();
         for (Connection c : conferenceableConnections) {
             // If statement checks for duplicates in input. It makes it N^2 but we're dealing with a
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 8368f42..23db6ac 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1036,6 +1036,26 @@
      */
     public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_RESET =
             "carrier_default_actions_on_reset_string_array";
+
+    /**
+     * Defines carrier-specific actions which act upon
+     * com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE,
+     * used for customization of the default carrier app
+     * Format:
+     * {
+     *     "true : CARRIER_ACTION_IDX_1",
+     *     "false: CARRIER_ACTION_IDX_2"
+     * }
+     * Where {@code true} is a boolean indicates default network available/unavailable
+     * Where {@code CARRIER_ACTION_IDX} is an integer defined in
+     * {@link com.android.carrierdefaultapp.CarrierActionUtils CarrierActionUtils}
+     * Example:
+     * {@link com.android.carrierdefaultapp.CarrierActionUtils
+     * #CARRIER_ACTION_ENABLE_DEFAULT_URL_HANDLER enable the app as the default URL handler}
+     * @hide
+     */
+    public static final String KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE =
+            "carrier_default_actions_on_default_network_available_string_array";
     /**
      * Defines a list of acceptable redirection url for default carrier app
      * @hides
@@ -1368,17 +1388,6 @@
             "notify_international_call_on_wfc_bool";
 
     /**
-     * Determine whether user edited tether APN (type dun) has effect
-     * {@code false} - Default. APN with dun type in telephony database has no effect.
-     *
-     * {@code true}  - DUN APN added/edited in ApnEditor will be used for tethering data call.
-     *
-     * @hide
-     */
-    public static final String KEY_EDITABLE_TETHER_APN_BOOL =
-            "editable_tether_apn_bool";
-
-    /**
      * An array containing custom call forwarding number prefixes that will be blocked while the
      * device is reporting that it is roaming. By default, there are no custom call
      * forwarding prefixes and none of these numbers will be filtered. If one or more entries are
@@ -1723,9 +1732,10 @@
         sDefaults.putString(KEY_CARRIER_SETUP_APP_STRING, "");
         sDefaults.putStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
                 new String[]{
-                        "com.android.carrierdefaultapp/.CarrierDefaultBroadcastReceiver:" +
-                                "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED," +
-                                "com.android.internal.telephony.CARRIER_SIGNAL_RESET"
+                        "com.android.carrierdefaultapp/.CarrierDefaultBroadcastReceiver:"
+                                + "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED,"
+                                + "com.android.internal.telephony.CARRIER_SIGNAL_RESET,"
+                                + "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE"
                 });
         sDefaults.putStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY, null);
 
@@ -1733,12 +1743,22 @@
         // Default carrier app configurations
         sDefaults.putStringArray(KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY,
                 new String[]{
-                        "4, 1"
+                        "9, 4, 1"
+                        //9: CARRIER_ACTION_REGISTER_NETWORK_AVAIL
                         //4: CARRIER_ACTION_DISABLE_METERED_APNS
                         //1: CARRIER_ACTION_SHOW_PORTAL_NOTIFICATION
                 });
         sDefaults.putStringArray(KEY_CARRIER_DEFAULT_ACTIONS_ON_RESET, new String[]{
-                "6" //6: CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS
+                "6, 8"
+                //6: CARRIER_ACTION_CANCEL_ALL_NOTIFICATIONS
+                //8: CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER
+                });
+        sDefaults.putStringArray(KEY_CARRIER_DEFAULT_ACTIONS_ON_DEFAULT_NETWORK_AVAILABLE,
+                new String[] {
+                        String.valueOf(false) + ": 7",
+                        //7: CARRIER_ACTION_ENABLE_DEFAULT_URL_HANDLER
+                        String.valueOf(true) + ": 8"
+                        //8: CARRIER_ACTION_DISABLE_DEFAULT_URL_HANDLER
                 });
         sDefaults.putStringArray(KEY_CARRIER_DEFAULT_REDIRECTION_URL_STRING_ARRAY, null);
 
@@ -1771,7 +1791,6 @@
         sDefaults.putBoolean(KEY_ALLOW_USSD_REQUESTS_VIA_TELEPHONY_MANAGER_BOOL, true);
         sDefaults.putBoolean(KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL, true);
         sDefaults.putBoolean(KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL, false);
-        sDefaults.putBoolean(KEY_EDITABLE_TETHER_APN_BOOL, false);
         sDefaults.putStringArray(KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY,
                 null);
         sDefaults.putInt(KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 2e7b19a..45339bb6 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1654,8 +1654,7 @@
      * @hide
      */
     public String getNetworkCountryIso(int subId) {
-        int phoneId = SubscriptionManager.getPhoneId(subId);
-        return getNetworkCountryIsoForPhone(phoneId);
+        return getNetworkCountryIsoForPhone(getPhoneId(subId));
     }
 
     /**
@@ -1670,7 +1669,13 @@
      */
     /** {@hide} */
     public String getNetworkCountryIsoForPhone(int phoneId) {
-        return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) return "";
+            return telephony.getNetworkCountryIsoForPhone(phoneId);
+        } catch (RemoteException ex) {
+            return "";
+        }
     }
 
     /** Network type is unknown */
@@ -6667,6 +6672,25 @@
     }
 
     /**
+     * Action set from carrier signalling broadcast receivers to start/stop reporting default
+     * network available events
+     * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+     * @param subId the subscription ID that this action applies to.
+     * @param report control start/stop reporting network status.
+     * @hide
+     */
+    public void carrierActionReportDefaultNetworkStatus(int subId, boolean report) {
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                service.carrierActionReportDefaultNetworkStatus(subId, report);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#carrierActionReportDefaultNetworkStatus", e);
+        }
+    }
+
+    /**
      * Get aggregated video call data usage since boot.
      * Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
      *
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index a0e5b7b..9262ec5 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -377,6 +377,13 @@
     Bundle getCellLocation(String callingPkg);
 
     /**
+     * Returns the ISO country code equivalent of the current registered
+     * operator's MCC (Mobile Country Code).
+     * @see android.telephony.TelephonyManager#getNetworkCountryIso
+     */
+    String getNetworkCountryIsoForPhone(int phoneId);
+
+    /**
      * Returns the neighboring cell information of the device.
      */
     List<NeighboringCellInfo> getNeighboringCellInfo(String callingPkg);
@@ -1296,6 +1303,16 @@
     void carrierActionSetRadioEnabled(int subId, boolean enabled);
 
     /**
+     * Action set from carrier signalling broadcast receivers to start/stop reporting default
+     * network conditions.
+     * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+     * @param subId the subscription ID that this action applies to.
+     * @param report control start/stop reporting default network events.
+     * @hide
+     */
+    void carrierActionReportDefaultNetworkStatus(int subId, boolean report);
+
+    /**
      * Get aggregated video call data usage since boot.
      * Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
      *
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 0343890..f29d993c 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -447,6 +447,20 @@
             "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE";
 
     /**
+     * <p>Broadcast Action: when system default network available/unavailable with
+     * carrier-disabled mobile data. Intended for carrier apps to set/reset carrier actions when
+     * other network becomes system default network, Wi-Fi for example.
+     * The intent will have the following extra values:</p>
+     * <ul>
+     *   <li>defaultNetworkAvailable</li><dd>A boolean indicates default network available.</dd>
+     *   <li>subId</li><dd>Sub Id which associated the default data.</dd>
+     * </ul>
+     * <p class="note">This is a protected intent that can only be sent by the system. </p>
+     */
+    public static final String ACTION_CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE =
+            "com.android.internal.telephony.CARRIER_SIGNAL_DEFAULT_NETWORK_AVAILABLE";
+
+    /**
      * <p>Broadcast Action: when framework reset all carrier actions on sim load or absent.
      * intended for carrier apps clean up (clear UI e.g.) and only sent to the specified carrier app
      * The intent will have the following extra values:</p>
@@ -465,7 +479,7 @@
     public static final String EXTRA_APN_PROTO_KEY = "apnProto";
     public static final String EXTRA_PCO_ID_KEY = "pcoId";
     public static final String EXTRA_PCO_VALUE_KEY = "pcoValue";
-
+    public static final String EXTRA_DEFAULT_NETWORK_AVAILABLE_KEY = "defaultNetworkAvailable";
 
    /**
      * Broadcast action to trigger CI OMA-DM Session.
diff --git a/test-runner/api/android-test-mock-current.txt b/test-runner/api/android-test-mock-current.txt
index 4063ed7..93bbf6c 100644
--- a/test-runner/api/android-test-mock-current.txt
+++ b/test-runner/api/android-test-mock-current.txt
@@ -292,6 +292,7 @@
     method public android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
     method public int getMoveStatus(int);
     method public java.lang.String getNameForUid(int);
+    method public java.lang.String[] getNamesForUids(int[]);
     method public java.util.List<android.os.storage.VolumeInfo> getPackageCandidateVolumes(android.content.pm.ApplicationInfo);
     method public android.os.storage.VolumeInfo getPackageCurrentVolume(android.content.pm.ApplicationInfo);
     method public int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 3cb1f39..68fd825 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -321,6 +321,11 @@
         throw new UnsupportedOperationException();
     }
 
+    @Override
+    public String[] getNamesForUids(int uid[]) {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * @hide - to match hiding in superclass
      */
diff --git a/tests/UiBench/Android.mk b/tests/UiBench/Android.mk
index e6af4b02..0824c26 100644
--- a/tests/UiBench/Android.mk
+++ b/tests/UiBench/Android.mk
@@ -12,6 +12,7 @@
 # regressions are reflected in test data
 LOCAL_RESOURCE_DIR := \
     $(LOCAL_PATH)/res \
+    frameworks/support/core-ui/res \
     frameworks/support/design/res \
     frameworks/support/v7/appcompat/res \
     frameworks/support/v7/cardview/res \
@@ -20,6 +21,7 @@
 
 LOCAL_AAPT_FLAGS := \
     --auto-add-overlay \
+    --extra-packages android.support.coreui \
     --extra-packages android.support.design \
     --extra-packages android.support.v7.appcompat \
     --extra-packages android.support.v7.cardview \
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index e4bf590..677585c 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -53,6 +53,7 @@
     libtinyxml2 \
     libvintf \
     libhwbinder \
+    libunwindstack \
     android.hidl.token@1.0
 
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
diff --git a/tests/net/OWNERS b/tests/net/OWNERS
index fa26997..2d71c20 100644
--- a/tests/net/OWNERS
+++ b/tests/net/OWNERS
@@ -1,6 +1,9 @@
 set noparent
 
 per-file Android.mk = build.master@android.com
+per-file Android.mk = ek@google.com
+per-file Android.mk = hugobenichi@google.com
+per-file Android.mk = lorenzo@google.com
 
 ek@google.com
 hugobenichi@google.com
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
index e3b06c8..7346f9f 100644
--- a/tests/net/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -21,10 +21,14 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.RESTRICTED_CAPABILITIES;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 
 
@@ -114,4 +118,45 @@
         assertFalse(netCap.hasCapability(NET_CAPABILITY_NOT_RESTRICTED));
     }
 
+    @Test
+    public void testDescribeImmutableDifferences() {
+        NetworkCapabilities nc1;
+        NetworkCapabilities nc2;
+
+        // Transports changing
+        nc1 = new NetworkCapabilities().addTransportType(TRANSPORT_CELLULAR);
+        nc2 = new NetworkCapabilities().addTransportType(TRANSPORT_WIFI);
+        assertNotEquals("", nc1.describeImmutableDifferences(nc2));
+        assertEquals("", nc1.describeImmutableDifferences(nc1));
+
+        // Mutable capability changing
+        nc1 = new NetworkCapabilities().addCapability(NET_CAPABILITY_VALIDATED);
+        nc2 = new NetworkCapabilities();
+        assertEquals("", nc1.describeImmutableDifferences(nc2));
+        assertEquals("", nc1.describeImmutableDifferences(nc1));
+
+        // NOT_METERED changing (http://b/63326103)
+        nc1 = new NetworkCapabilities()
+                .addCapability(NET_CAPABILITY_NOT_METERED)
+                .addCapability(NET_CAPABILITY_INTERNET);
+        nc2 = new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET);
+        assertEquals("", nc1.describeImmutableDifferences(nc2));
+        assertEquals("", nc1.describeImmutableDifferences(nc1));
+
+        // Immutable capability changing
+        nc1 = new NetworkCapabilities()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
+        nc2 = new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET);
+        assertNotEquals("", nc1.describeImmutableDifferences(nc2));
+        assertEquals("", nc1.describeImmutableDifferences(nc1));
+
+        // Specifier changing
+        nc1 = new NetworkCapabilities().addTransportType(TRANSPORT_WIFI);
+        nc2 = new NetworkCapabilities()
+                .addTransportType(TRANSPORT_WIFI)
+                .setNetworkSpecifier(new StringNetworkSpecifier("specs"));
+        assertNotEquals("", nc1.describeImmutableDifferences(nc2));
+        assertEquals("", nc1.describeImmutableDifferences(nc1));
+    }
 }
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index a115146..099cfd4 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -40,6 +40,7 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.mock;
 
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -59,12 +60,14 @@
 import android.net.util.SharedLog;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiManager;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.INetworkManagementService;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 import android.os.test.TestLooper;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -558,6 +561,90 @@
         verifyNoMoreInteractions(mNMService);
     }
 
+    private void userRestrictionsListenerBehaviour(
+        boolean currentDisallow, boolean nextDisallow, String[] activeTetheringIfacesList,
+        int expectedInteractionsWithShowNotification) throws  Exception {
+        final int userId = 0;
+        final Bundle currRestrictions = new Bundle();
+        final Bundle newRestrictions = new Bundle();
+        Tethering tethering = mock(Tethering.class);
+        Tethering.TetheringUserRestrictionListener turl =
+                new Tethering.TetheringUserRestrictionListener(tethering);
+
+        currRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, currentDisallow);
+        newRestrictions.putBoolean(UserManager.DISALLOW_CONFIG_TETHERING, nextDisallow);
+        when(tethering.getTetheredIfaces()).thenReturn(activeTetheringIfacesList);
+
+        turl.onUserRestrictionsChanged(userId, newRestrictions, currRestrictions);
+
+        verify(tethering, times(expectedInteractionsWithShowNotification))
+                .showTetheredNotification(anyInt(), eq(false));
+
+        verify(tethering, times(expectedInteractionsWithShowNotification)).untetherAll();
+    }
+
+    @Test
+    public void testDisallowTetheringWhenNoTetheringInterfaceIsActive() throws Exception {
+        final String[] emptyActiveIfacesList = new String[]{};
+        final boolean currDisallow = false;
+        final boolean nextDisallow = true;
+        final int expectedInteractionsWithShowNotification = 0;
+
+        userRestrictionsListenerBehaviour(currDisallow, nextDisallow, emptyActiveIfacesList,
+                expectedInteractionsWithShowNotification);
+    }
+
+    @Test
+    public void testDisallowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception {
+        final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
+        final boolean currDisallow = false;
+        final boolean nextDisallow = true;
+        final int expectedInteractionsWithShowNotification = 1;
+
+        userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+                expectedInteractionsWithShowNotification);
+    }
+
+    @Test
+    public void testAllowTetheringWhenNoTetheringInterfaceIsActive() throws Exception {
+        final String[] nonEmptyActiveIfacesList = new String[]{};
+        final boolean currDisallow = true;
+        final boolean nextDisallow = false;
+        final int expectedInteractionsWithShowNotification = 0;
+
+        userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+                expectedInteractionsWithShowNotification);
+    }
+
+    @Test
+    public void testAllowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception {
+        final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
+        final boolean currDisallow = true;
+        final boolean nextDisallow = false;
+        final int expectedInteractionsWithShowNotification = 0;
+
+        userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+                expectedInteractionsWithShowNotification);
+    }
+
+    @Test
+    public void testDisallowTetheringUnchanged() throws Exception {
+        final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname};
+        final int expectedInteractionsWithShowNotification = 0;
+        boolean currDisallow = true;
+        boolean nextDisallow = true;
+
+        userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+                expectedInteractionsWithShowNotification);
+
+        currDisallow = false;
+        nextDisallow = false;
+
+        userRestrictionsListenerBehaviour(currDisallow, nextDisallow, nonEmptyActiveIfacesList,
+                expectedInteractionsWithShowNotification);
+    }
+
+
     // TODO: Test that a request for hotspot mode doesn't interfere with an
     // already operating tethering mode interface.
 }
diff --git a/tools/aapt2/Format.proto b/tools/aapt2/Format.proto
index 8381fa0..6be7f02 100644
--- a/tools/aapt2/Format.proto
+++ b/tools/aapt2/Format.proto
@@ -67,21 +67,12 @@
 
 // Top level message representing a resource table.
 message ResourceTable {
-  // The string pool containing string values (strings that actually end up in the binary ARSC
-  // file) referenced throughout the resource table.
-  optional StringPool string_pool = 1;
-
   // The string pool containing source paths referenced throughout the resource table. This does
   // not end up in the final binary ARSC file.
-  optional StringPool source_pool = 2;
-
-  // The string pool containing the names of unresolved symbols. This does not end up in the final
-  // binary ARSC file. Unresolved symbols are just resource names that haven't had a resource ID
-  // assigned to them, therefore can't be referenced by resource ID.
-  optional StringPool symbol_pool = 3;
+  optional StringPool source_pool = 1;
 
   // Resource definitions corresponding to an Android package.
-  repeated Package packages = 4;
+  repeated Package packages = 2;
 }
 
 // Defines resources for an Android package.
@@ -202,9 +193,10 @@
   optional Reference ref = 1;
   optional String str = 2;
   optional RawString raw_str = 3;
-  optional FileReference file = 4;
-  optional Id id = 5;
-  optional Primitive prim = 6;
+  optional StyledString styled_str = 4;
+  optional FileReference file = 5;
+  optional Id id = 6;
+  optional Primitive prim = 7;
 }
 
 // A CompoundValue is an abstract type. It represents a value that is a made of other values.
@@ -233,10 +225,8 @@
   // The resource ID (0xPPTTEEEE) of the resource being referred.
   optional uint32 id = 2;
 
-  // If the resource ID is not resolved, the index into the symbol string pool where the name of
-  // the reference is stored. The symbol string pool is located at the top level ResourceTable
-  // message.
-  optional uint32 symbol_idx = 3;
+  // The optional resource name.
+  optional string name = 3;
 
   // Whether this reference is referencing a private resource (@*package:type/entry).
   optional bool private = 4;
@@ -249,23 +239,41 @@
 
 // A value that is a string.
 message String {
-  // The index into the values string pool, located at the top level ResourceTable message.
-  optional uint32 idx = 1;
+  optional string value = 1;
 }
 
 // A value that is a raw string, which is unescaped/uninterpreted. This is typically used to
 // represent the value of a style attribute before the attribute is compiled and the set of
 // allowed values is known.
 message RawString {
-  // The index into the values string pool, located at the top level ResourceTable message.
-  optional uint32 idx = 1;
+  optional string value = 1;
+}
+
+// A string with styling information, like html tags that specify boldness, italics, etc.
+message StyledString {
+  // The raw text of the string.
+  optional string value = 1;
+
+  // A Span marks a region of the string text that is styled.
+  message Span {
+    // The name of the tag, and its attributes, encoded as follows:
+    // tag_name;attr1=value1;attr2=value2;[...]
+    optional string tag = 1;
+
+    // The first character position this span applies to, in UTF-16 offset.
+    optional uint32 first_char = 2;
+
+    // The last character position this span applies to, in UTF-16 offset.
+    optional uint32 last_char = 3;
+  }
+
+  repeated Span span = 2;
 }
 
 // A value that is a reference to an external entity, like an XML file or a PNG.
 message FileReference {
-  // The index into the values string pool, located at the top level ResourceTable message. This
-  // represents the path to the file within an APK (typically res/type-config/entry.ext).
-  optional uint32 path_idx = 1;
+  // Path to a file within the APK (typically res/type-config/entry.ext).
+  optional string path = 1;
 }
 
 // A value that represents a primitive data type (float, int, boolean, etc.).
diff --git a/tools/aapt2/LoadedApk.cpp b/tools/aapt2/LoadedApk.cpp
index 7e5efa1..abc0e4c 100644
--- a/tools/aapt2/LoadedApk.cpp
+++ b/tools/aapt2/LoadedApk.cpp
@@ -84,7 +84,7 @@
 
     std::string path = file->GetSource().path;
     // The name of the path has the format "<zip-file-name>@<path-to-file>".
-    path = path.substr(path.find("@") + 1);
+    path = path.substr(path.find('@') + 1);
 
     // Skip resources that are not referenced if requested.
     if (path.find("res/") == 0 && referenced_resources.find(path) == referenced_resources.end()) {
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index e6bf3a6..3e3f432 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -915,7 +915,7 @@
 
   bool WriteJavaFile(ResourceTable* table, const StringPiece& package_name_to_generate,
                      const StringPiece& out_package, const JavaClassGeneratorOptions& java_options,
-                     const Maybe<std::string> out_text_symbols_path = {}) {
+                     const Maybe<std::string>& out_text_symbols_path = {}) {
     if (!options_.generate_java_class_path) {
       return true;
     }
diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp
index 37d5ed0..e891954 100644
--- a/tools/aapt2/proto/TableProtoDeserializer.cpp
+++ b/tools/aapt2/proto/TableProtoDeserializer.cpp
@@ -55,15 +55,10 @@
 
 class PackagePbDeserializer {
  public:
-  PackagePbDeserializer(const android::ResStringPool* valuePool,
-                        const android::ResStringPool* sourcePool,
-                        const android::ResStringPool* symbolPool,
-                        const Source& source, IDiagnostics* diag)
-      : value_pool_(valuePool),
-        source_pool_(sourcePool),
-        symbol_pool_(symbolPool),
-        source_(source),
-        diag_(diag) {}
+  PackagePbDeserializer(const android::ResStringPool* sourcePool, const Source& source,
+                        IDiagnostics* diag)
+      : source_pool_(sourcePool), source_(source), diag_(diag) {
+  }
 
  public:
   bool DeserializeFromPb(const pb::Package& pbPackage, ResourceTable* table) {
@@ -87,8 +82,7 @@
       for (const pb::Entry& pbEntry : pbType.entries()) {
         ResourceEntry* entry = type->FindOrCreateEntry(pbEntry.name());
 
-        // Deserialize the symbol status (public/private with source and
-        // comments).
+        // Deserialize the symbol status (public/private with source and comments).
         if (pbEntry.has_symbol_status()) {
           const pb::SymbolStatus& pbStatus = pbEntry.symbol_status();
           if (pbStatus.has_source()) {
@@ -161,8 +155,7 @@
 
  private:
   std::unique_ptr<Item> DeserializeItemFromPb(const pb::Item& pb_item,
-                                              const ConfigDescription& config,
-                                              StringPool* pool) {
+                                              const ConfigDescription& config, StringPool* pool) {
     if (pb_item.has_ref()) {
       const pb::Reference& pb_ref = pb_item.ref();
       std::unique_ptr<Reference> ref = util::make_unique<Reference>();
@@ -173,45 +166,32 @@
 
     } else if (pb_item.has_prim()) {
       const pb::Primitive& pb_prim = pb_item.prim();
-      android::Res_value prim = {};
-      prim.dataType = static_cast<uint8_t>(pb_prim.type());
-      prim.data = pb_prim.data();
-      return util::make_unique<BinaryPrimitive>(prim);
+      return util::make_unique<BinaryPrimitive>(static_cast<uint8_t>(pb_prim.type()),
+                                                pb_prim.data());
 
     } else if (pb_item.has_id()) {
       return util::make_unique<Id>();
 
     } else if (pb_item.has_str()) {
-      const uint32_t idx = pb_item.str().idx();
-      const std::string str = util::GetString(*value_pool_, idx);
-
-      const android::ResStringPool_span* spans = value_pool_->styleAt(idx);
-      if (spans && spans->name.index != android::ResStringPool_span::END) {
-        StyleString style_str = {str};
-        while (spans->name.index != android::ResStringPool_span::END) {
-          style_str.spans.push_back(
-              Span{util::GetString(*value_pool_, spans->name.index),
-                   spans->firstChar, spans->lastChar});
-          spans++;
-        }
-        return util::make_unique<StyledString>(pool->MakeRef(
-            style_str, StringPool::Context(StringPool::Context::kNormalPriority, config)));
-      }
       return util::make_unique<String>(
-          pool->MakeRef(str, StringPool::Context(config)));
+          pool->MakeRef(pb_item.str().value(), StringPool::Context(config)));
 
     } else if (pb_item.has_raw_str()) {
-      const uint32_t idx = pb_item.raw_str().idx();
-      const std::string str = util::GetString(*value_pool_, idx);
       return util::make_unique<RawString>(
-          pool->MakeRef(str, StringPool::Context(config)));
+          pool->MakeRef(pb_item.raw_str().value(), StringPool::Context(config)));
+
+    } else if (pb_item.has_styled_str()) {
+      const pb::StyledString& pb_str = pb_item.styled_str();
+      StyleString style_str{pb_str.value()};
+      for (const pb::StyledString::Span& pb_span : pb_str.span()) {
+        style_str.spans.push_back(Span{pb_span.tag(), pb_span.first_char(), pb_span.last_char()});
+      }
+      return util::make_unique<StyledString>(pool->MakeRef(
+          style_str, StringPool::Context(StringPool::Context::kNormalPriority, config)));
 
     } else if (pb_item.has_file()) {
-      const uint32_t idx = pb_item.file().path_idx();
-      const std::string str = util::GetString(*value_pool_, idx);
       return util::make_unique<FileReference>(pool->MakeRef(
-          str,
-          StringPool::Context(StringPool::Context::kHighPriority, config)));
+          pb_item.file().path(), StringPool::Context(StringPool::Context::kHighPriority, config)));
 
     } else {
       diag_->Error(DiagMessage(source_) << "unknown item");
@@ -255,15 +235,13 @@
         std::unique_ptr<Style> style = util::make_unique<Style>();
         if (pb_style.has_parent()) {
           style->parent = Reference();
-          if (!DeserializeReferenceFromPb(pb_style.parent(),
-                                          &style->parent.value())) {
+          if (!DeserializeReferenceFromPb(pb_style.parent(), &style->parent.value())) {
             return {};
           }
 
           if (pb_style.has_parent_source()) {
             Source parent_source;
-            DeserializeSourceFromPb(pb_style.parent_source(), *source_pool_,
-                                    &parent_source);
+            DeserializeSourceFromPb(pb_style.parent_source(), *source_pool_, &parent_source);
             style->parent.value().SetSource(std::move(parent_source));
           }
         }
@@ -300,8 +278,7 @@
         const pb::Array& pb_array = pb_compound_value.array();
         std::unique_ptr<Array> array = util::make_unique<Array>();
         for (const pb::Array_Entry& pb_entry : pb_array.entries()) {
-          std::unique_ptr<Item> item =
-              DeserializeItemFromPb(pb_entry.item(), config, pool);
+          std::unique_ptr<Item> item = DeserializeItemFromPb(pb_entry.item(), config, pool);
           if (!item) {
             return {};
           }
@@ -316,8 +293,7 @@
         std::unique_ptr<Plural> plural = util::make_unique<Plural>();
         for (const pb::Plural_Entry& pb_entry : pb_plural.entries()) {
           size_t pluralIdx = DeserializePluralEnumFromPb(pb_entry.arity());
-          plural->values[pluralIdx] =
-              DeserializeItemFromPb(pb_entry.item(), config, pool);
+          plural->values[pluralIdx] = DeserializeItemFromPb(pb_entry.item(), config, pool);
           if (!plural->values[pluralIdx]) {
             return {};
           }
@@ -350,11 +326,10 @@
       out_ref->id = ResourceId(pb_ref.id());
     }
 
-    if (pb_ref.has_symbol_idx()) {
-      const std::string str_symbol = util::GetString(*symbol_pool_, pb_ref.symbol_idx());
+    if (pb_ref.has_name()) {
       ResourceNameRef name_ref;
-      if (!ResourceUtils::ParseResourceName(str_symbol, &name_ref, nullptr)) {
-        diag_->Error(DiagMessage(source_) << "invalid reference name '" << str_symbol << "'");
+      if (!ResourceUtils::ParseResourceName(pb_ref.name(), &name_ref, nullptr)) {
+        diag_->Error(DiagMessage(source_) << "invalid reference name '" << pb_ref.name() << "'");
         return false;
       }
 
@@ -377,60 +352,32 @@
   }
 
  private:
-  const android::ResStringPool* value_pool_;
   const android::ResStringPool* source_pool_;
-  const android::ResStringPool* symbol_pool_;
   const Source source_;
   IDiagnostics* diag_;
 };
 
 }  // namespace
 
-std::unique_ptr<ResourceTable> DeserializeTableFromPb(
-    const pb::ResourceTable& pb_table, const Source& source,
-    IDiagnostics* diag) {
-  // We import the android namespace because on Windows NO_ERROR is a macro, not
-  // an enum, which
+std::unique_ptr<ResourceTable> DeserializeTableFromPb(const pb::ResourceTable& pb_table,
+                                                      const Source& source, IDiagnostics* diag) {
+  // We import the android namespace because on Windows NO_ERROR is a macro, not an enum, which
   // causes errors when qualifying it with android::
   using namespace android;
 
   std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>();
 
-  if (!pb_table.has_string_pool()) {
-    diag->Error(DiagMessage(source) << "no string pool found");
-    return {};
-  }
-
-  ResStringPool value_pool;
-  status_t result = value_pool.setTo(pb_table.string_pool().data().data(),
-                                     pb_table.string_pool().data().size());
-  if (result != NO_ERROR) {
-    diag->Error(DiagMessage(source) << "invalid string pool");
-    return {};
-  }
-
   ResStringPool source_pool;
   if (pb_table.has_source_pool()) {
-    result = source_pool.setTo(pb_table.source_pool().data().data(),
-                               pb_table.source_pool().data().size());
+    status_t result = source_pool.setTo(pb_table.source_pool().data().data(),
+                                        pb_table.source_pool().data().size());
     if (result != NO_ERROR) {
       diag->Error(DiagMessage(source) << "invalid source pool");
       return {};
     }
   }
 
-  ResStringPool symbol_pool;
-  if (pb_table.has_symbol_pool()) {
-    result = symbol_pool.setTo(pb_table.symbol_pool().data().data(),
-                               pb_table.symbol_pool().data().size());
-    if (result != NO_ERROR) {
-      diag->Error(DiagMessage(source) << "invalid symbol pool");
-      return {};
-    }
-  }
-
-  PackagePbDeserializer package_pb_deserializer(&value_pool, &source_pool,
-                                                &symbol_pool, source, diag);
+  PackagePbDeserializer package_pb_deserializer(&source_pool, source, diag);
   for (const pb::Package& pb_package : pb_table.packages()) {
     if (!package_pb_deserializer.DeserializeFromPb(pb_package, table.get())) {
       return {};
diff --git a/tools/aapt2/proto/TableProtoSerializer.cpp b/tools/aapt2/proto/TableProtoSerializer.cpp
index 730442c..3f48655 100644
--- a/tools/aapt2/proto/TableProtoSerializer.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer.cpp
@@ -24,9 +24,9 @@
 
 #include "android-base/logging.h"
 
-using google::protobuf::io::CodedOutputStream;
-using google::protobuf::io::CodedInputStream;
-using google::protobuf::io::ZeroCopyOutputStream;
+using ::google::protobuf::io::CodedInputStream;
+using ::google::protobuf::io::CodedOutputStream;
+using ::google::protobuf::io::ZeroCopyOutputStream;
 
 namespace aapt {
 
@@ -36,46 +36,46 @@
  public:
   using RawValueVisitor::Visit;
 
-  /**
-   * Constructor to use when expecting to serialize any value.
-   */
-  PbSerializerVisitor(StringPool* source_pool, StringPool* symbol_pool,
-                      pb::Value* out_pb_value)
-      : source_pool_(source_pool),
-        symbol_pool_(symbol_pool),
-        out_pb_value_(out_pb_value),
-        out_pb_item_(nullptr) {}
+  // Constructor to use when expecting to serialize any value.
+  PbSerializerVisitor(StringPool* source_pool, pb::Value* out_pb_value)
+      : source_pool_(source_pool), out_pb_value_(out_pb_value), out_pb_item_(nullptr) {
+  }
 
-  /**
-   * Constructor to use when expecting to serialize an Item.
-   */
-  PbSerializerVisitor(StringPool* sourcePool, StringPool* symbolPool,
-                      pb::Item* outPbItem)
-      : source_pool_(sourcePool),
-        symbol_pool_(symbolPool),
-        out_pb_value_(nullptr),
-        out_pb_item_(outPbItem) {}
+  // Constructor to use when expecting to serialize an Item.
+  PbSerializerVisitor(StringPool* sourcePool, pb::Item* outPbItem)
+      : source_pool_(sourcePool), out_pb_value_(nullptr), out_pb_item_(outPbItem) {
+  }
 
   void Visit(Reference* ref) override {
     SerializeReferenceToPb(*ref, pb_item()->mutable_ref());
   }
 
   void Visit(String* str) override {
-    pb_item()->mutable_str()->set_idx(str->value.index());
+    pb_item()->mutable_str()->set_value(*str->value);
+  }
+
+  void Visit(RawString* str) override {
+    pb_item()->mutable_raw_str()->set_value(*str->value);
   }
 
   void Visit(StyledString* str) override {
-    pb_item()->mutable_str()->set_idx(str->value.index());
+    pb::StyledString* pb_str = pb_item()->mutable_styled_str();
+    pb_str->set_value(str->value->value);
+
+    for (const StringPool::Span& span : str->value->spans) {
+      pb::StyledString::Span* pb_span = pb_str->add_span();
+      pb_span->set_tag(*span.name);
+      pb_span->set_first_char(span.first_char);
+      pb_span->set_last_char(span.last_char);
+    }
   }
 
   void Visit(FileReference* file) override {
-    pb_item()->mutable_file()->set_path_idx(file->path.index());
+    pb_item()->mutable_file()->set_path(*file->path);
   }
 
-  void Visit(Id* id) override { pb_item()->mutable_id(); }
-
-  void Visit(RawString* raw_str) override {
-    pb_item()->mutable_raw_str()->set_idx(raw_str->value.index());
+  void Visit(Id* /*id*/) override {
+    pb_item()->mutable_id();
   }
 
   void Visit(BinaryPrimitive* prim) override {
@@ -119,7 +119,7 @@
 
       pb::Item* pb_item = pb_entry->mutable_item();
       SerializeItemCommonToPb(entry.key, pb_entry);
-      PbSerializerVisitor sub_visitor(source_pool_, symbol_pool_, pb_item);
+      PbSerializerVisitor sub_visitor(source_pool_, pb_item);
       entry.value->Accept(&sub_visitor);
     }
   }
@@ -138,8 +138,7 @@
     for (auto& value : array->items) {
       pb::Array_Entry* pb_entry = pb_array->add_entries();
       SerializeItemCommonToPb(*value, pb_entry);
-      PbSerializerVisitor sub_visitor(source_pool_, symbol_pool_,
-                                      pb_entry->mutable_item());
+      PbSerializerVisitor sub_visitor(source_pool_, pb_entry->mutable_item());
       value->Accept(&sub_visitor);
     }
   }
@@ -157,7 +156,7 @@
       pb_entry->set_arity(SerializePluralEnumToPb(i));
       pb::Item* pb_element = pb_entry->mutable_item();
       SerializeItemCommonToPb(*plural->values[i], pb_entry);
-      PbSerializerVisitor sub_visitor(source_pool_, symbol_pool_, pb_element);
+      PbSerializerVisitor sub_visitor(source_pool_, pb_element);
       plural->values[i]->Accept(&sub_visitor);
     }
   }
@@ -179,8 +178,7 @@
 
   template <typename T>
   void SerializeItemCommonToPb(const Item& item, T* pb_item) {
-    SerializeSourceToPb(item.GetSource(), source_pool_,
-                        pb_item->mutable_source());
+    SerializeSourceToPb(item.GetSource(), source_pool_, pb_item->mutable_source());
     if (!item.GetComment().empty()) {
       pb_item->set_comment(item.GetComment());
     }
@@ -192,8 +190,7 @@
     }
 
     if (ref.name) {
-      StringPool::Ref symbol_ref = symbol_pool_->MakeRef(ref.name.value().ToString());
-      pb_ref->set_symbol_idx(static_cast<uint32_t>(symbol_ref.index()));
+      pb_ref->set_name(ref.name.value().ToString());
     }
 
     pb_ref->set_private_(ref.private_reference);
@@ -201,7 +198,6 @@
   }
 
   StringPool* source_pool_;
-  StringPool* symbol_pool_;
   pb::Value* out_pb_value_;
   pb::Item* out_pb_item_;
 };
@@ -220,9 +216,7 @@
   });
 
   auto pb_table = util::make_unique<pb::ResourceTable>();
-  SerializeStringPoolToPb(table->string_pool, pb_table->mutable_string_pool());
-
-  StringPool source_pool, symbol_pool;
+  StringPool source_pool;
 
   for (auto& package : table->packages) {
     pb::Package* pb_package = pb_table->add_packages();
@@ -270,7 +264,7 @@
             pb_value->set_weak(true);
           }
 
-          PbSerializerVisitor visitor(&source_pool, &symbol_pool, pb_value);
+          PbSerializerVisitor visitor(&source_pool, pb_value);
           config_value->value->Accept(&visitor);
         }
       }
@@ -278,7 +272,6 @@
   }
 
   SerializeStringPoolToPb(source_pool, pb_table->mutable_source_pool());
-  SerializeStringPoolToPb(symbol_pool, pb_table->mutable_symbol_pool());
   return pb_table;
 }
 
diff --git a/tools/aapt2/proto/TableProtoSerializer_test.cpp b/tools/aapt2/proto/TableProtoSerializer_test.cpp
index 3ebb08e..3ba4e6b 100644
--- a/tools/aapt2/proto/TableProtoSerializer_test.cpp
+++ b/tools/aapt2/proto/TableProtoSerializer_test.cpp
@@ -20,7 +20,9 @@
 #include "test/Test.h"
 
 using ::google::protobuf::io::StringOutputStream;
+using ::testing::Eq;
 using ::testing::NotNull;
+using ::testing::SizeIs;
 
 namespace aapt {
 
@@ -38,12 +40,12 @@
 
   Symbol public_symbol;
   public_symbol.state = SymbolState::kPublic;
-  ASSERT_TRUE(table->SetSymbolState(
-      test::ParseNameOrDie("com.app.a:layout/main"), ResourceId(0x7f020000),
-      public_symbol, context->GetDiagnostics()));
+  ASSERT_TRUE(table->SetSymbolState(test::ParseNameOrDie("com.app.a:layout/main"),
+                                    ResourceId(0x7f020000), public_symbol,
+                                    context->GetDiagnostics()));
 
   Id* id = test::GetValue<Id>(table.get(), "com.app.a:id/foo");
-  ASSERT_NE(nullptr, id);
+  ASSERT_THAT(id, NotNull());
 
   // Make a plural.
   std::unique_ptr<Plural> plural = util::make_unique<Plural>();
@@ -52,6 +54,15 @@
                                  ConfigDescription{}, {}, std::move(plural),
                                  context->GetDiagnostics()));
 
+  // Make a styled string.
+  StyleString style_string;
+  style_string.str = "hello";
+  style_string.spans.push_back(Span{"b", 0u, 4u});
+  ASSERT_TRUE(
+      table->AddResource(test::ParseNameOrDie("com.app.a:string/styled"), ConfigDescription{}, {},
+                         util::make_unique<StyledString>(table->string_pool.MakeRef(style_string)),
+                         context->GetDiagnostics()));
+
   // Make a resource with different products.
   ASSERT_TRUE(table->AddResource(
       test::ParseNameOrDie("com.app.a:integer/one"),
@@ -65,9 +76,8 @@
       context->GetDiagnostics()));
 
   // Make a reference with both resource name and resource ID.
-  // The reference should point to a resource outside of this table to test that
-  // both
-  // name and id get serialized.
+  // The reference should point to a resource outside of this table to test that both name and id
+  // get serialized.
   Reference expected_ref;
   expected_ref.name = test::ParseNameOrDie("android:layout/main");
   expected_ref.id = ResourceId(0x01020000);
@@ -85,36 +95,45 @@
 
   Id* new_id = test::GetValue<Id>(new_table.get(), "com.app.a:id/foo");
   ASSERT_THAT(new_id, NotNull());
-  EXPECT_EQ(id->IsWeak(), new_id->IsWeak());
+  EXPECT_THAT(new_id->IsWeak(), Eq(id->IsWeak()));
 
   Maybe<ResourceTable::SearchResult> result =
       new_table->FindResource(test::ParseNameOrDie("com.app.a:layout/main"));
   ASSERT_TRUE(result);
-  EXPECT_EQ(SymbolState::kPublic, result.value().type->symbol_status.state);
-  EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbol_status.state);
+
+  EXPECT_THAT(result.value().type->symbol_status.state, Eq(SymbolState::kPublic));
+  EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kPublic));
 
   result = new_table->FindResource(test::ParseNameOrDie("com.app.a:bool/foo"));
   ASSERT_TRUE(result);
-  EXPECT_EQ(SymbolState::kUndefined, result.value().entry->symbol_status.state);
+  EXPECT_THAT(result.value().entry->symbol_status.state, Eq(SymbolState::kUndefined));
   EXPECT_TRUE(result.value().entry->symbol_status.allow_new);
 
   // Find the product-dependent values
   BinaryPrimitive* prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
       new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), "");
   ASSERT_THAT(prim, NotNull());
-  EXPECT_EQ(123u, prim->value.data);
+  EXPECT_THAT(prim->value.data, Eq(123u));
 
   prim = test::GetValueForConfigAndProduct<BinaryPrimitive>(
       new_table.get(), "com.app.a:integer/one", test::ParseConfigOrDie("land"), "tablet");
   ASSERT_THAT(prim, NotNull());
-  EXPECT_EQ(321u, prim->value.data);
+  EXPECT_THAT(prim->value.data, Eq(321u));
 
   Reference* actual_ref = test::GetValue<Reference>(new_table.get(), "com.app.a:layout/abc");
   ASSERT_THAT(actual_ref, NotNull());
   ASSERT_TRUE(actual_ref->name);
   ASSERT_TRUE(actual_ref->id);
-  EXPECT_EQ(expected_ref.name.value(), actual_ref->name.value());
-  EXPECT_EQ(expected_ref.id.value(), actual_ref->id.value());
+  EXPECT_THAT(*actual_ref, Eq(expected_ref));
+
+  StyledString* actual_styled_str =
+      test::GetValue<StyledString>(new_table.get(), "com.app.a:string/styled");
+  ASSERT_THAT(actual_styled_str, NotNull());
+  EXPECT_THAT(actual_styled_str->value->value, Eq("hello"));
+  ASSERT_THAT(actual_styled_str->value->spans, SizeIs(1u));
+  EXPECT_THAT(*actual_styled_str->value->spans[0].name, Eq("b"));
+  EXPECT_THAT(actual_styled_str->value->spans[0].first_char, Eq(0u));
+  EXPECT_THAT(actual_styled_str->value->spans[0].last_char, Eq(4u));
 }
 
 TEST(TableProtoSerializer, SerializeFileHeader) {
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index 51a75d7..a9b49d9 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -520,11 +520,10 @@
   return !(*this == rhs);
 }
 
-Tokenizer::iterator::iterator(StringPiece s, char sep, StringPiece tok,
-                              bool end)
+Tokenizer::iterator::iterator(const StringPiece& s, char sep, const StringPiece& tok, bool end)
     : str_(s), separator_(sep), token_(tok), end_(end) {}
 
-Tokenizer::Tokenizer(StringPiece str, char sep)
+Tokenizer::Tokenizer(const StringPiece& str, char sep)
     : begin_(++iterator(str, sep, StringPiece(str.begin() - 1, 0), false)),
       end_(str, sep, StringPiece(str.end(), 0), true) {}
 
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index ad3989e..f89f8bf 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -240,7 +240,7 @@
    private:
     friend class Tokenizer;
 
-    iterator(android::StringPiece s, char sep, android::StringPiece tok, bool end);
+    iterator(const android::StringPiece& s, char sep, const android::StringPiece& tok, bool end);
 
     android::StringPiece str_;
     char separator_;
@@ -248,7 +248,7 @@
     bool end_;
   };
 
-  Tokenizer(android::StringPiece str, char sep);
+  Tokenizer(const android::StringPiece& str, char sep);
 
   iterator begin() { return begin_; }
 
diff --git a/tools/bit/main.cpp b/tools/bit/main.cpp
index a7fbc2e..91ca514 100644
--- a/tools/bit/main.cpp
+++ b/tools/bit/main.cpp
@@ -50,6 +50,7 @@
 
     int testPassCount;
     int testFailCount;
+    int unknownFailureCount; // unknown failure == "Process crashed", etc.
     bool actionsWithNoTests;
 
     Target(bool b, bool i, bool t, const string& p);
@@ -63,6 +64,7 @@
      testActionCount(0),
      testPassCount(0),
      testFailCount(0),
+     unknownFailureCount(0),
      actionsWithNoTests(false)
 {
 }
@@ -188,8 +190,13 @@
      */
     void SetCurrentAction(TestAction* action);
 
+    bool IsSuccess();
+
+    string GetErrorMessage();
+
 private:
     TestAction* m_currentAction;
+    SessionStatus m_sessionStatus;
 };
 
 void
@@ -241,8 +248,10 @@
         }
         line << ": " << m_currentAction->target->name << ':' << className << "\\#" << testName;
         print_one_line("%s", line.str().c_str());
-    } else if (resultCode == -2) {
+    } else if ((resultCode == -1) || (resultCode == -2)) {
         // test failed
+        // Note -2 means an assertion failure, and -1 means other exceptions.  We just treat them
+        // all as "failures".
         m_currentAction->failCount++;
         m_currentAction->target->testFailCount++;
         printf("%s\n%sFailed: %s:%s\\#%s%s\n", g_escapeClearLine, g_escapeRedBold,
@@ -257,9 +266,13 @@
 }
 
 void
-TestResults::OnSessionStatus(SessionStatus& /*status*/)
+TestResults::OnSessionStatus(SessionStatus& status)
 {
     //status.PrintDebugString();
+    m_sessionStatus = status;
+    if (m_currentAction && !IsSuccess()) {
+        m_currentAction->target->unknownFailureCount++;
+    }
 }
 
 void
@@ -268,6 +281,24 @@
     m_currentAction = action;
 }
 
+bool
+TestResults::IsSuccess()
+{
+    return m_sessionStatus.result_code() == -1; // Activity.RESULT_OK.
+}
+
+string
+TestResults::GetErrorMessage()
+{
+    bool found;
+    string shortMsg = get_bundle_string(m_sessionStatus.results(), &found, "shortMsg", NULL);
+    if (!found) {
+        return IsSuccess() ? "" : "Unknown failure";
+    }
+    return shortMsg;
+}
+
+
 /**
  * Prints the usage statement / help text.
  */
@@ -568,7 +599,7 @@
 /**
  * Run the build, install, and test actions.
  */
-void
+bool
 run_phases(vector<Target*> targets, const Options& options)
 {
     int err = 0;
@@ -837,6 +868,10 @@
                 printf("%s%d passed%s, %d failed\n", g_escapeGreenBold, action.passCount,
                         g_escapeEndColor, action.failCount);
             }
+            if (!testResults.IsSuccess()) {
+                printf("\n%sTest didn't finish successfully: %s%s\n", g_escapeRedBold,
+                        testResults.GetErrorMessage().c_str(), g_escapeEndColor);
+            }
         }
     }
 
@@ -907,6 +942,7 @@
     }
 
     // Tests
+    bool hasErrors = false;
     if (testActions.size() > 0) {
         printf("%sRan tests:%s\n", g_escapeBold, g_escapeEndColor);
         size_t maxNameLength = 0;
@@ -924,12 +960,18 @@
             Target* target = targets[i];
             if (target->testActionCount > 0) {
                 printf("   %s%s", target->name.c_str(), padding.c_str() + target->name.length());
-                if (target->actionsWithNoTests) {
+                if (target->unknownFailureCount > 0) {
+                    printf("     %sUnknown failure, see above message.%s\n",
+                            g_escapeRedBold, g_escapeEndColor);
+                    hasErrors = true;
+                } else if (target->actionsWithNoTests) {
                     printf("     %s%d passed, %d failed%s\n", g_escapeYellowBold,
                             target->testPassCount, target->testFailCount, g_escapeEndColor);
+                    hasErrors = true;
                 } else if (target->testFailCount > 0) {
                     printf("     %d passed, %s%d failed%s\n", target->testPassCount,
                             g_escapeRedBold, target->testFailCount, g_escapeEndColor);
+                    hasErrors = true;
                 } else {
                     printf("     %s%d passed%s, %d failed\n", g_escapeGreenBold,
                             target->testPassCount, g_escapeEndColor, target->testFailCount);
@@ -944,6 +986,7 @@
     }
 
     printf("%s--------------------------------------------%s\n", g_escapeBold, g_escapeEndColor);
+    return !hasErrors;
 }
 
 /**
@@ -991,7 +1034,7 @@
         exit(0);
     } else {
         // Normal run
-        run_phases(options.targets, options);
+        exit(run_phases(options.targets, options) ? 0 : 1);
     }
 
     return 0;
diff --git a/tools/locked_region_code_injection/Android.mk b/tools/locked_region_code_injection/Android.mk
index 0aed0ce..d921783 100644
--- a/tools/locked_region_code_injection/Android.mk
+++ b/tools/locked_region_code_injection/Android.mk
@@ -9,7 +9,7 @@
     asm-5.2 \
     asm-commons-5.2 \
     asm-tree-5.2 \
-    asm-analysis-5.2
-
+    asm-analysis-5.2 \
+    guava-20.0 \
 
 include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java
index 9374f23..99ef8a7 100644
--- a/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java
+++ b/tools/locked_region_code_injection/src/lockedregioncodeinjection/LockFindingClassVisitor.java
@@ -18,6 +18,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
 import org.objectweb.asm.MethodVisitor;
 import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.commons.TryCatchBlockSorter;
@@ -32,6 +33,10 @@
 import org.objectweb.asm.tree.analysis.BasicValue;
 import org.objectweb.asm.tree.analysis.Frame;
 
+import static com.google.common.base.Preconditions.checkElementIndex;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+
 /**
  * This visitor does two things:
  *
@@ -140,10 +145,26 @@
                     if (operand instanceof LockTargetState) {
                         LockTargetState state = (LockTargetState) operand;
                         for (int j = 0; j < state.getTargets().size(); j++) {
+                            // The instruction after a monitor_exit should be a label for the end of the implicit
+                            // catch block that surrounds the synchronized block to call monitor_exit when an exception
+                            // occurs.
+                            checkState(instructions.get(i + 1).getType() == AbstractInsnNode.LABEL,
+                                "Expected to find label after monitor exit");
+
+                            int labelIndex = i + 1;
+                            checkElementIndex(labelIndex, instructions.size());
+
+                            LabelNode label = (LabelNode)instructions.get(labelIndex);
+
+                            checkNotNull(handlersMap.get(i));
+                            checkElementIndex(0, handlersMap.get(i).size());
+                            checkState(handlersMap.get(i).get(0).end == label,
+                                "Expected label to be the end of monitor exit's try block");
+
                             LockTarget target = state.getTargets().get(j);
                             MethodInsnNode call = new MethodInsnNode(Opcodes.INVOKESTATIC,
                                     target.getPostOwner(), target.getPostMethod(), "()V", false);
-                            insertMethodCallAfter(mn, frameMap, handlersMap, s, i, call);
+                            insertMethodCallAfter(mn, frameMap, handlersMap, label, labelIndex, call);
                         }
                     }
                 }
diff --git a/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java b/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java
index 1d4f2d4..b86954d 100644
--- a/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java
+++ b/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestMain.java
@@ -228,4 +228,26 @@
         Assert.assertEquals(TestTarget.unboostCount, 1);
         Assert.assertEquals(TestTarget.invokeCount, 1);
     }
+
+    @Test
+    public void testUnboostThatThrows() {
+        TestTarget.resetCount();
+        TestTarget t = new TestTarget();
+        boolean asserted = false;
+
+        Assert.assertEquals(TestTarget.boostCount, 0);
+        Assert.assertEquals(TestTarget.unboostCount, 0);
+
+        try {
+            t.synchronizedThrowsOnUnboost();
+        } catch (RuntimeException e) {
+            asserted = true;
+        }
+
+        Assert.assertEquals(asserted, true);
+        Assert.assertEquals(TestTarget.boostCount, 1);
+        Assert.assertEquals(TestTarget.unboostCount, 0);
+        Assert.assertEquals(TestTarget.invokeCount, 1);
+    }
+
 }
diff --git a/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestTarget.java b/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestTarget.java
index 8e7d478..d1c8f34 100644
--- a/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestTarget.java
+++ b/tools/locked_region_code_injection/test/lockedregioncodeinjection/TestTarget.java
@@ -17,12 +17,17 @@
   public static int boostCount = 0;
   public static int unboostCount = 0;
   public static int invokeCount = 0;
+  public static boolean nextUnboostThrows = false;
 
   public static void boost() {
     boostCount++;
   }
 
   public static void unboost() {
+    if (nextUnboostThrows) {
+      nextUnboostThrows = false;
+      throw new RuntimeException();
+    }
     unboostCount++;
   }
 
@@ -49,4 +54,11 @@
     invoke();
     return this;
   }
+
+  public void synchronizedThrowsOnUnboost() {
+    nextUnboostThrows = true;
+    synchronized(this) {
+      invoke();
+    }
+  }
 }
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 7972d06..a3a1054 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -94,7 +94,7 @@
 
     void reassociate();
 
-    WifiInfo getConnectionInfo();
+    WifiInfo getConnectionInfo(String callingPackage);
 
     boolean setWifiEnabled(String packageName, boolean enable);
 
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 4dc7862..a367b23 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -151,8 +151,9 @@
     /**
      * This factor is used to adjust the rate output under the new algorithm
      * such that the result is comparable to the previous algorithm.
+     * This actually converts from unit 'packets per second' to 'packets per 5 seconds'.
      */
-    private static final long OUTPUT_SCALE_FACTOR = 5000;
+    private static final long OUTPUT_SCALE_FACTOR = 5;
     private long mLastPacketCountUpdateTimeStamp;
 
     /**
@@ -198,16 +199,16 @@
                     double currentSampleWeight = 1.0 - lastSampleWeight;
 
                     txBadRate = txBadRate * lastSampleWeight
-                        + (txbad - txBad) * OUTPUT_SCALE_FACTOR / timeDelta
+                        + (txbad - txBad) * OUTPUT_SCALE_FACTOR * 1000 / timeDelta
                         * currentSampleWeight;
                     txSuccessRate = txSuccessRate * lastSampleWeight
-                        + (txgood - txSuccess) * OUTPUT_SCALE_FACTOR / timeDelta
+                        + (txgood - txSuccess) * OUTPUT_SCALE_FACTOR * 1000 / timeDelta
                         * currentSampleWeight;
                     rxSuccessRate = rxSuccessRate * lastSampleWeight
-                        + (rxgood - rxSuccess) * OUTPUT_SCALE_FACTOR / timeDelta
+                        + (rxgood - rxSuccess) * OUTPUT_SCALE_FACTOR * 1000 / timeDelta
                         * currentSampleWeight;
                     txRetriesRate = txRetriesRate * lastSampleWeight
-                        + (txretries - txRetries) * OUTPUT_SCALE_FACTOR / timeDelta
+                        + (txretries - txRetries) * OUTPUT_SCALE_FACTOR * 1000/ timeDelta
                         * currentSampleWeight;
             } else {
                 txBadRate = 0;
@@ -448,6 +449,22 @@
     }
 
     /**
+     * @hide
+     * This returns txSuccessRate in packets per second.
+     */
+    public double getTxSuccessRatePps() {
+        return txSuccessRate / OUTPUT_SCALE_FACTOR;
+    }
+
+    /**
+     * @hide
+     * This returns rxSuccessRate in packets per second.
+     */
+    public double getRxSuccessRatePps() {
+        return rxSuccessRate / OUTPUT_SCALE_FACTOR;
+    }
+
+    /**
      * Record the MAC address of the WLAN interface
      * @param macAddress the MAC address in {@code XX:XX:XX:XX:XX:XX} form
      * @hide
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index c499edc..bc37810 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -1643,7 +1643,7 @@
      */
     public WifiInfo getConnectionInfo() {
         try {
-            return mService.getConnectionInfo();
+            return mService.getConnectionInfo(mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }