Merge changes from topic "stream_combinations"

* changes:
  Camera: Implement legacy device stream combination query
  Camera: Add reprocessable mandatory stream combinations
  Camera: Add support for mandatory stream combinations
diff --git a/Android.bp b/Android.bp
index 32bd408..e096c9d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -535,6 +535,7 @@
         "telephony/java/android/telephony/ims/aidl/IImsServiceController.aidl",
         "telephony/java/android/telephony/ims/aidl/IImsServiceControllerListener.aidl",
         "telephony/java/android/telephony/ims/aidl/IImsSmsListener.aidl",
+        "telephony/java/android/telephony/ims/aidl/IRcs.aidl",
         "telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
         "telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
         "telephony/java/android/telephony/mbms/IMbmsGroupCallSessionCallback.aidl",
@@ -611,7 +612,6 @@
         "telephony/java/com/android/internal/telephony/euicc/ISetDefaultSmdpAddressCallback.aidl",
         "telephony/java/com/android/internal/telephony/euicc/ISetNicknameCallback.aidl",
         "telephony/java/com/android/internal/telephony/euicc/ISwitchToProfileCallback.aidl",
-        "telephony/java/com/android/internal/telephony/rcs/IRcs.aidl",
         "wifi/java/android/net/wifi/INetworkRequestMatchCallback.aidl",
         "wifi/java/android/net/wifi/INetworkRequestUserSelectionCallback.aidl",
         "wifi/java/android/net/wifi/ISoftApCallback.aidl",
diff --git a/Android.mk b/Android.mk
index 92e33e9..e3cc275 100644
--- a/Android.mk
+++ b/Android.mk
@@ -72,6 +72,11 @@
 	$(hide) mkdir -p $(OUT_DOCS)/offline-sdk
 	( unzip -qo $< -d $(OUT_DOCS)/offline-sdk && touch -f $@ ) || exit 1
 
+# Run this for checkbuild
+checkbuild: doc-comment-check-docs
+# Check comment when you are updating the API
+update-api: doc-comment-check-docs
+
 # ==== hiddenapi lists =======================================
 .KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS)
 $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
diff --git a/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java b/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java
new file mode 100644
index 0000000..a482c4a
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/textclassifier/TextClassifierPerfTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 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.textclassifier;
+
+import android.content.Context;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.LargeTest;
+import android.view.textclassifier.ConversationActions;
+import android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLanguage;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Random;
+
+@RunWith(Parameterized.class)
+@LargeTest
+public class TextClassifierPerfTest {
+    /** Request contains meaning text, rather than garbled text. */
+    private static final int ACTUAL_REQUEST = 0;
+    private static final String RANDOM_CHAR_SET = "abcdefghijklmnopqrstuvwxyz0123456789";
+
+    @Rule
+    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Parameterized.Parameters(name = "size={0}")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][]{{ACTUAL_REQUEST}, {10}, {100}, {1000}});
+    }
+
+    private TextClassifier mTextClassifier;
+    private final int mSize;
+
+    public TextClassifierPerfTest(int size) {
+        mSize = size;
+    }
+
+    @Before
+    public void setUp() {
+        Context context = InstrumentationRegistry.getTargetContext();
+        TextClassificationManager textClassificationManager =
+                context.getSystemService(TextClassificationManager.class);
+        mTextClassifier = textClassificationManager.getTextClassifier();
+    }
+
+    @Test
+    public void testSuggestConversationActions() {
+        String text = mSize == ACTUAL_REQUEST ? "Where are you?" : generateRandomString(mSize);
+        ConversationActions.Request request = createConversationActionsRequest(text);
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mTextClassifier.suggestConversationActions(request);
+        }
+    }
+
+    @Test
+    public void testDetectLanguage() {
+        String text = mSize == ACTUAL_REQUEST
+                ? "これは日本語のテキストです" : generateRandomString(mSize);
+        TextLanguage.Request request = createTextLanguageRequest(text);
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            mTextClassifier.detectLanguage(request);
+        }
+    }
+
+    private static ConversationActions.Request createConversationActionsRequest(CharSequence text) {
+        ConversationActions.Message message =
+                new ConversationActions.Message.Builder(
+                        ConversationActions.Message.PERSON_USER_REMOTE)
+                        .setText(text)
+                        .build();
+        return new ConversationActions.Request.Builder(Collections.singletonList(message))
+                .build();
+    }
+
+    private static TextLanguage.Request createTextLanguageRequest(CharSequence text) {
+        return new TextLanguage.Request.Builder(text).build();
+    }
+
+    private static String generateRandomString(int length) {
+        Random random = new Random();
+        StringBuilder stringBuilder = new StringBuilder(length);
+        for (int i = 0; i < length; i++) {
+            int index = random.nextInt(RANDOM_CHAR_SET.length());
+            stringBuilder.append(RANDOM_CHAR_SET.charAt(index));
+        }
+        return stringBuilder.toString();
+    }
+}
diff --git a/api/current.txt b/api/current.txt
index 693248a..40f2d67 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -57,6 +57,7 @@
     field public static final java.lang.String BROADCAST_SMS = "android.permission.BROADCAST_SMS";
     field public static final java.lang.String BROADCAST_STICKY = "android.permission.BROADCAST_STICKY";
     field public static final java.lang.String BROADCAST_WAP_PUSH = "android.permission.BROADCAST_WAP_PUSH";
+    field public static final java.lang.String CALL_COMPANION_APP = "android.permission.CALL_COMPANION_APP";
     field public static final java.lang.String CALL_PHONE = "android.permission.CALL_PHONE";
     field public static final java.lang.String CALL_PRIVILEGED = "android.permission.CALL_PRIVILEGED";
     field public static final java.lang.String CAMERA = "android.permission.CAMERA";
@@ -91,7 +92,6 @@
     field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
     field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
     field public static final java.lang.String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
-    field public static final java.lang.String CALL_COMPANION_APP = "android.permission.CALL_COMPANION_APP";
     field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
     field public static final java.lang.String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
     field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
@@ -11227,6 +11227,15 @@
     field public static final int FLAG_MATCH_PINNED_BY_ANY_LAUNCHER = 1024; // 0x400
   }
 
+  public final class ModuleInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.lang.String getName();
+    method public java.lang.String getPackageName();
+    method public boolean isHidden();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.content.pm.ModuleInfo> CREATOR;
+  }
+
   public class PackageInfo implements android.os.Parcelable {
     ctor public PackageInfo();
     method public int describeContents();
@@ -11443,6 +11452,7 @@
     method public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method public abstract android.graphics.drawable.Drawable getDrawable(java.lang.String, int, android.content.pm.ApplicationInfo);
     method public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
+    method public java.util.List<android.content.pm.ModuleInfo> getInstalledModules(int);
     method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
     method public abstract java.lang.String getInstallerPackageName(java.lang.String);
     method public abstract byte[] getInstantAppCookie();
@@ -11450,6 +11460,7 @@
     method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String);
     method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String);
+    method public android.content.pm.ModuleInfo getModuleInfo(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract java.lang.String getNameForUid(int);
     method public android.content.pm.PackageInfo getPackageArchiveInfo(java.lang.String, int);
     method public abstract int[] getPackageGids(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -13561,6 +13572,46 @@
     ctor public BitmapShader(android.graphics.Bitmap, android.graphics.Shader.TileMode, android.graphics.Shader.TileMode);
   }
 
+  public final class BlendMode extends java.lang.Enum {
+    method public static android.graphics.BlendMode valueOf(java.lang.String);
+    method public static final android.graphics.BlendMode[] values();
+    enum_constant public static final android.graphics.BlendMode CLEAR;
+    enum_constant public static final android.graphics.BlendMode COLOR;
+    enum_constant public static final android.graphics.BlendMode COLOR_BURN;
+    enum_constant public static final android.graphics.BlendMode COLOR_DODGE;
+    enum_constant public static final android.graphics.BlendMode DARKEN;
+    enum_constant public static final android.graphics.BlendMode DIFFERENCE;
+    enum_constant public static final android.graphics.BlendMode DST;
+    enum_constant public static final android.graphics.BlendMode DST_ATOP;
+    enum_constant public static final android.graphics.BlendMode DST_IN;
+    enum_constant public static final android.graphics.BlendMode DST_OUT;
+    enum_constant public static final android.graphics.BlendMode DST_OVER;
+    enum_constant public static final android.graphics.BlendMode EXCLUSION;
+    enum_constant public static final android.graphics.BlendMode HARD_LIGHT;
+    enum_constant public static final android.graphics.BlendMode HUE;
+    enum_constant public static final android.graphics.BlendMode LIGHTEN;
+    enum_constant public static final android.graphics.BlendMode LUMINOSITY;
+    enum_constant public static final android.graphics.BlendMode MODULATE;
+    enum_constant public static final android.graphics.BlendMode MULTIPLY;
+    enum_constant public static final android.graphics.BlendMode OVERLAY;
+    enum_constant public static final android.graphics.BlendMode PLUS;
+    enum_constant public static final android.graphics.BlendMode SATURATION;
+    enum_constant public static final android.graphics.BlendMode SCREEN;
+    enum_constant public static final android.graphics.BlendMode SOFT_LIGHT;
+    enum_constant public static final android.graphics.BlendMode SRC;
+    enum_constant public static final android.graphics.BlendMode SRC_ATOP;
+    enum_constant public static final android.graphics.BlendMode SRC_IN;
+    enum_constant public static final android.graphics.BlendMode SRC_OUT;
+    enum_constant public static final android.graphics.BlendMode SRC_OVER;
+    enum_constant public static final android.graphics.BlendMode XOR;
+  }
+
+  public final class BlendModeColorFilter extends android.graphics.ColorFilter {
+    ctor public BlendModeColorFilter(int, android.graphics.BlendMode);
+    method public int getColor();
+    method public android.graphics.BlendMode getMode();
+  }
+
   public class BlurMaskFilter extends android.graphics.MaskFilter {
     ctor public BlurMaskFilter(float, android.graphics.BlurMaskFilter.Blur);
   }
@@ -13623,7 +13674,8 @@
     method public void drawBitmapMesh(android.graphics.Bitmap, int, int, float[], int, int[], int, android.graphics.Paint);
     method public void drawCircle(float, float, float, android.graphics.Paint);
     method public void drawColor(int);
-    method public void drawColor(int, android.graphics.PorterDuff.Mode);
+    method public deprecated void drawColor(int, android.graphics.PorterDuff.Mode);
+    method public void drawColor(int, android.graphics.BlendMode);
     method public void drawDoubleRoundRect(android.graphics.RectF, float, float, android.graphics.RectF, float, float, android.graphics.Paint);
     method public void drawDoubleRoundRect(android.graphics.RectF, float[], android.graphics.RectF, float[], android.graphics.Paint);
     method public void drawLine(float, float, float, float, android.graphics.Paint);
@@ -14245,6 +14297,7 @@
     method public float descent();
     method public boolean equalsForTextMeasurement(android.graphics.Paint);
     method public int getAlpha();
+    method public android.graphics.BlendMode getBlendMode();
     method public int getColor();
     method public android.graphics.ColorFilter getColorFilter();
     method public boolean getFillPath(android.graphics.Path, android.graphics.Path);
@@ -14299,7 +14352,7 @@
     method public float getUnderlinePosition();
     method public float getUnderlineThickness();
     method public float getWordSpacing();
-    method public android.graphics.Xfermode getXfermode();
+    method public deprecated android.graphics.Xfermode getXfermode();
     method public boolean hasGlyph(java.lang.String);
     method public final boolean isAntiAlias();
     method public final boolean isDither();
@@ -14319,6 +14372,7 @@
     method public void setARGB(int, int, int, int);
     method public void setAlpha(int);
     method public void setAntiAlias(boolean);
+    method public void setBlendMode(android.graphics.BlendMode);
     method public void setColor(int);
     method public android.graphics.ColorFilter setColorFilter(android.graphics.ColorFilter);
     method public void setDither(boolean);
@@ -14352,7 +14406,7 @@
     method public android.graphics.Typeface setTypeface(android.graphics.Typeface);
     method public void setUnderlineText(boolean);
     method public void setWordSpacing(float);
-    method public android.graphics.Xfermode setXfermode(android.graphics.Xfermode);
+    method public deprecated android.graphics.Xfermode setXfermode(android.graphics.Xfermode);
     field public static final int ANTI_ALIAS_FLAG = 1; // 0x1
     field public static final int CURSOR_AFTER = 0; // 0x0
     field public static final int CURSOR_AT = 4; // 0x4
@@ -14634,7 +14688,7 @@
     enum_constant public static final android.graphics.PorterDuff.Mode XOR;
   }
 
-  public class PorterDuffColorFilter extends android.graphics.ColorFilter {
+  public deprecated class PorterDuffColorFilter extends android.graphics.ColorFilter {
     ctor public PorterDuffColorFilter(int, android.graphics.PorterDuff.Mode);
   }
 
@@ -15190,7 +15244,7 @@
     method public final void setCallback(android.graphics.drawable.Drawable.Callback);
     method public void setChangingConfigurations(int);
     method public abstract void setColorFilter(android.graphics.ColorFilter);
-    method public void setColorFilter(int, android.graphics.PorterDuff.Mode);
+    method public deprecated void setColorFilter(int, android.graphics.PorterDuff.Mode);
     method public deprecated void setDither(boolean);
     method public void setFilterBitmap(boolean);
     method public void setHotspot(float, float);
@@ -26279,9 +26333,12 @@
     field public static final int SUCCESS = 0; // 0x0
   }
 
-  public static class AudioEffect.Descriptor {
+  public static final class AudioEffect.Descriptor implements android.os.Parcelable {
     ctor public AudioEffect.Descriptor();
     ctor public AudioEffect.Descriptor(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.media.audiofx.AudioEffect.Descriptor> CREATOR;
     field public java.lang.String connectMode;
     field public java.lang.String implementor;
     field public java.lang.String name;
@@ -37492,25 +37549,37 @@
     method public static android.net.Uri buildSearchDocumentsUri(java.lang.String, java.lang.String, java.lang.String);
     method public static android.net.Uri buildTreeDocumentUri(java.lang.String, java.lang.String);
     method public static android.net.Uri copyDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+    method public static deprecated android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
     method public static android.net.Uri createDocument(android.content.ContentInterface, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
+    method public static deprecated android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
     method public static android.content.IntentSender createWebLinkIntent(android.content.ContentInterface, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
+    method public static deprecated android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
     method public static boolean deleteDocument(android.content.ContentInterface, android.net.Uri) throws java.io.FileNotFoundException;
+    method public static deprecated boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
     method public static void ejectRoot(android.content.ContentInterface, android.net.Uri);
+    method public static deprecated void ejectRoot(android.content.ContentResolver, android.net.Uri);
     method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentInterface, android.net.Uri) throws java.io.FileNotFoundException;
+    method public static deprecated android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
     method public static java.lang.String getDocumentId(android.net.Uri);
     method public static android.os.Bundle getDocumentMetadata(android.content.ContentInterface, android.net.Uri) throws java.io.FileNotFoundException;
+    method public static deprecated android.os.Bundle getDocumentMetadata(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
     method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentInterface, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
+    method public static deprecated android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
     method public static java.lang.String getRootId(android.net.Uri);
     method public static java.lang.String getSearchDocumentsQuery(android.net.Uri);
     method public static java.lang.String getTreeDocumentId(android.net.Uri);
     method public static boolean isChildDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+    method public static deprecated boolean isChildDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
     method public static boolean isDocumentUri(android.content.Context, android.net.Uri);
     method public static boolean isRootUri(android.content.Context, android.net.Uri);
     method public static boolean isRootsUri(android.content.Context, android.net.Uri);
     method public static boolean isTreeUri(android.net.Uri);
     method public static android.net.Uri moveDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+    method public static deprecated android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
     method public static boolean removeDocument(android.content.ContentInterface, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
+    method public static deprecated boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
     method public static android.net.Uri renameDocument(android.content.ContentInterface, android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
+    method public static deprecated android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
     field public static final java.lang.String ACTION_DOCUMENT_SETTINGS = "android.provider.action.DOCUMENT_SETTINGS";
     field public static final java.lang.String EXTRA_ERROR = "error";
     field public static final java.lang.String EXTRA_EXCLUDE_SELF = "android.provider.extra.EXCLUDE_SELF";
@@ -43181,6 +43250,7 @@
     field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
     field public static final java.lang.String EXTRA_INCOMING_VIDEO_STATE = "android.telecom.extra.INCOMING_VIDEO_STATE";
     field public static final java.lang.String EXTRA_IS_DEFAULT_CALL_SCREENING_APP = "android.telecom.extra.IS_DEFAULT_CALL_SCREENING_APP";
+    field public static final java.lang.String EXTRA_IS_ENABLED = "android.telecom.extra.IS_ENABLED";
     field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telecom.extra.NOTIFICATION_COUNT";
     field public static final java.lang.String EXTRA_NOTIFICATION_PHONE_NUMBER = "android.telecom.extra.NOTIFICATION_PHONE_NUMBER";
     field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
@@ -43188,14 +43258,13 @@
     field public static final java.lang.String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT";
     field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
     field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
-    field public static final java.lang.String EXTRA_IS_ENABLED = "android.telecom.extra.IS_ENABLED";
     field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
     field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
     field public static final java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS";
     field public static final java.lang.String METADATA_INCLUDE_SELF_MANAGED_CALLS = "android.telecom.INCLUDE_SELF_MANAGED_CALLS";
+    field public static final java.lang.String METADATA_IN_CALL_SERVICE_CAR_MODE_UI = "android.telecom.IN_CALL_SERVICE_CAR_MODE_UI";
     field public static final java.lang.String METADATA_IN_CALL_SERVICE_RINGING = "android.telecom.IN_CALL_SERVICE_RINGING";
     field public static final java.lang.String METADATA_IN_CALL_SERVICE_UI = "android.telecom.IN_CALL_SERVICE_UI";
-    field public static final java.lang.String METADATA_IN_CALL_SERVICE_CAR_MODE_UI = "android.telecom.IN_CALL_SERVICE_CAR_MODE_UI";
     field public static final int PRESENTATION_ALLOWED = 1; // 0x1
     field public static final int PRESENTATION_PAYPHONE = 4; // 0x4
     field public static final int PRESENTATION_RESTRICTED = 2; // 0x2
@@ -48906,7 +48975,9 @@
     method public float getPressure();
     method public float getPressure(int);
     method public float getRawX();
+    method public float getRawX(int);
     method public float getRawY();
+    method public float getRawY(int);
     method public float getSize();
     method public float getSize(int);
     method public int getSource();
diff --git a/api/removed.txt b/api/removed.txt
index e3e8b63..f7106d2 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -508,21 +508,6 @@
     field public static final deprecated java.lang.String TIMESTAMP = "timestamp";
   }
 
-  public final class DocumentsContract {
-    method public static android.net.Uri copyDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
-    method public static android.net.Uri createDocument(android.content.ContentResolver, android.net.Uri, java.lang.String, java.lang.String) throws java.io.FileNotFoundException;
-    method public static android.content.IntentSender createWebLinkIntent(android.content.ContentResolver, android.net.Uri, android.os.Bundle) throws java.io.FileNotFoundException;
-    method public static boolean deleteDocument(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
-    method public static void ejectRoot(android.content.ContentResolver, android.net.Uri);
-    method public static android.provider.DocumentsContract.Path findDocumentPath(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
-    method public static android.os.Bundle getDocumentMetadata(android.content.ContentResolver, android.net.Uri) throws java.io.FileNotFoundException;
-    method public static android.graphics.Bitmap getDocumentThumbnail(android.content.ContentResolver, android.net.Uri, android.graphics.Point, android.os.CancellationSignal) throws java.io.FileNotFoundException;
-    method public static boolean isChildDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
-    method public static android.net.Uri moveDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
-    method public static boolean removeDocument(android.content.ContentResolver, android.net.Uri, android.net.Uri) throws java.io.FileNotFoundException;
-    method public static android.net.Uri renameDocument(android.content.ContentResolver, android.net.Uri, java.lang.String) throws java.io.FileNotFoundException;
-  }
-
   public static final class Settings.Global extends android.provider.Settings.NameValueTable {
     field public static final deprecated java.lang.String CONTACT_METADATA_SYNC = "contact_metadata_sync";
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index da6840c..5a49f0bf3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6559,6 +6559,7 @@
     field public static final int CODE_RADIO_SETUP_FAILURE = 1509; // 0x5e5
     field public static final int CODE_RADIO_UPLINK_FAILURE = 1508; // 0x5e4
     field public static final int CODE_REGISTRATION_ERROR = 1000; // 0x3e8
+    field public static final int CODE_REJECTED_ELSEWHERE = 1017; // 0x3f9
     field public static final int CODE_REJECT_1X_COLLISION = 1603; // 0x643
     field public static final int CODE_REJECT_CALL_ON_OTHER_SUB = 1602; // 0x642
     field public static final int CODE_REJECT_CALL_TYPE_NOT_ALLOWED = 1605; // 0x645
@@ -6582,26 +6583,39 @@
     field public static final int CODE_REJECT_VT_AVPF_NOT_ALLOWED = 1619; // 0x653
     field public static final int CODE_REJECT_VT_TTY_NOT_ALLOWED = 1615; // 0x64f
     field public static final int CODE_REMOTE_CALL_DECLINE = 1404; // 0x57c
+    field public static final int CODE_SESSION_MODIFICATION_FAILED = 1517; // 0x5ed
     field public static final int CODE_SIP_ALTERNATE_EMERGENCY_CALL = 1514; // 0x5ea
+    field public static final int CODE_SIP_AMBIGUOUS = 376; // 0x178
     field public static final int CODE_SIP_BAD_ADDRESS = 337; // 0x151
     field public static final int CODE_SIP_BAD_REQUEST = 331; // 0x14b
     field public static final int CODE_SIP_BUSY = 338; // 0x152
+    field public static final int CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST = 372; // 0x174
     field public static final int CODE_SIP_CLIENT_ERROR = 342; // 0x156
+    field public static final int CODE_SIP_EXTENSION_REQUIRED = 370; // 0x172
     field public static final int CODE_SIP_FORBIDDEN = 332; // 0x14c
     field public static final int CODE_SIP_GLOBAL_ERROR = 362; // 0x16a
+    field public static final int CODE_SIP_INTERVAL_TOO_BRIEF = 371; // 0x173
+    field public static final int CODE_SIP_LOOP_DETECTED = 373; // 0x175
+    field public static final int CODE_SIP_METHOD_NOT_ALLOWED = 366; // 0x16e
     field public static final int CODE_SIP_NOT_ACCEPTABLE = 340; // 0x154
     field public static final int CODE_SIP_NOT_FOUND = 333; // 0x14d
     field public static final int CODE_SIP_NOT_REACHABLE = 341; // 0x155
     field public static final int CODE_SIP_NOT_SUPPORTED = 334; // 0x14e
+    field public static final int CODE_SIP_PROXY_AUTHENTICATION_REQUIRED = 367; // 0x16f
     field public static final int CODE_SIP_REDIRECTED = 321; // 0x141
     field public static final int CODE_SIP_REQUEST_CANCELLED = 339; // 0x153
+    field public static final int CODE_SIP_REQUEST_ENTITY_TOO_LARGE = 368; // 0x170
+    field public static final int CODE_SIP_REQUEST_PENDING = 377; // 0x179
     field public static final int CODE_SIP_REQUEST_TIMEOUT = 335; // 0x14f
+    field public static final int CODE_SIP_REQUEST_URI_TOO_LARGE = 369; // 0x171
     field public static final int CODE_SIP_SERVER_ERROR = 354; // 0x162
     field public static final int CODE_SIP_SERVER_INTERNAL_ERROR = 351; // 0x15f
     field public static final int CODE_SIP_SERVER_TIMEOUT = 353; // 0x161
     field public static final int CODE_SIP_SERVICE_UNAVAILABLE = 352; // 0x160
     field public static final int CODE_SIP_TEMPRARILY_UNAVAILABLE = 336; // 0x150
     field public static final int CODE_SIP_TRANSACTION_DOES_NOT_EXIST = 343; // 0x157
+    field public static final int CODE_SIP_TOO_MANY_HOPS = 374; // 0x176
+    field public static final int CODE_SIP_UNDECIPHERABLE = 378; // 0x17a
     field public static final int CODE_SIP_USER_MARKED_UNWANTED = 365; // 0x16d
     field public static final int CODE_SIP_USER_REJECTED = 361; // 0x169
     field public static final int CODE_SUPP_SVC_CANCELLED = 1202; // 0x4b2
@@ -6611,9 +6625,11 @@
     field public static final int CODE_TIMEOUT_NO_ANSWER = 202; // 0xca
     field public static final int CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE = 203; // 0xcb
     field public static final int CODE_UNSPECIFIED = 0; // 0x0
+    field public static final int CODE_USER_CANCELLED_SESSION_MODIFICATION = 512; // 0x200
     field public static final int CODE_USER_DECLINE = 504; // 0x1f8
     field public static final int CODE_USER_IGNORE = 503; // 0x1f7
     field public static final int CODE_USER_NOANSWER = 502; // 0x1f6
+    field public static final int CODE_USER_REJECTED_SESSION_MODIFICATION = 511; // 0x1ff
     field public static final int CODE_USER_TERMINATED = 501; // 0x1f5
     field public static final int CODE_USER_TERMINATED_BY_REMOTE = 510; // 0x1fe
     field public static final int CODE_UT_CB_PASSWORD_MISMATCH = 821; // 0x335
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index f58caff..410bd19 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -2394,16 +2394,23 @@
 }
 
 /**
- * Pulls low power state information. This includes platform and subsystem sleep state information,
- * PowerStatePlatformSleepState, PowerStateVoter or PowerStateSubsystemSleepState as defined in
+ * Pulls low power state information. If power.stats HAL is not available, this
+ * includes platform and subsystem sleep state information,
+ * PowerStatePlatformSleepState, PowerStateVoter or PowerStateSubsystemSleepState
+ * as defined in:
  *   hardware/interfaces/power/1.0/types.hal
  *   hardware/interfaces/power/1.1/types.hal
+ * If power.stats HAL is available, this includes PowerEntityStateResidencyResult
+ * as defined in:
+ *   hardware/interfaces/power/stats/1.0/types.hal
  */
 message SubsystemSleepState {
     // Subsystem name
     optional string subsystem_name = 1;
     // For PlatformLowPowerStats (hal 1.0), this is the voter name, which could be empty.
     // For SubsystemLowPowerStats (hal 1.1), this is the sleep state name.
+    // For PowerEntityStateResidencyResult (hal power/stats/1.0) this is the
+    //    powerEntityStateName from the corresponding PowerEntityStateInfo.
     optional string subname = 2;
     // The number of times it entered, or voted for entering the sleep state
     optional uint64 count = 3;
@@ -2540,15 +2547,19 @@
 
 /*
  * Logs the memory stats for a process.
+ *
+ * Pulled from StatsCompanionService for all managed processes (from ActivityManagerService).
  */
 message ProcessMemoryState {
     // The uid if available. -1 means not available.
     optional int32 uid = 1 [(is_uid) = true];
 
     // The process name.
+    // Usually package name, "system" for system server.
+    // Provided by ActivityManagerService.
     optional string process_name = 2;
 
-    // oom adj score.
+    // Current OOM score adjustment. Value read from ProcessRecord.
     optional int32 oom_adj_score = 3;
 
     // # of page-faults
@@ -2558,12 +2569,18 @@
     optional int64 page_major_fault = 5;
 
     // RSS
+    // Value is read from /proc/PID/stat, field 24. Or from memory.stat, field
+    // total_rss if per-app memory cgroups are enabled.
     optional int64 rss_in_bytes = 6;
 
     // CACHE
+    // Value is read from memory.stat, field total_cache if per-app memory
+    // cgroups are enabled. Otherwise, 0.
     optional int64 cache_in_bytes = 7;
 
     // SWAP
+    // Value is read from memory.stat, field total_swap if per-app memory
+    // cgroups are enabled. Otherwise, 0.
     optional int64 swap_in_bytes = 8;
 
     // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
@@ -2576,12 +2593,15 @@
 
 /*
  * Logs the memory stats for a native process (from procfs).
+ *
+ * Pulled from StatsCompanionService for selected native processes.
  */
 message NativeProcessMemoryState {
     // The uid if available. -1 means not available.
     optional int32 uid = 1 [(is_uid) = true];
 
     // The process name.
+    // Value read from /proc/PID/cmdline.
     optional string process_name = 2;
 
     // # of page-faults
@@ -2591,6 +2611,7 @@
     optional int64 page_major_fault = 4;
 
     // RSS
+    // Value read from /proc/PID/stat, field 24.
     optional int64 rss_in_bytes = 5;
 
     // Deprecated: use ProcessMemoryHighWaterMark atom instead. Always 0.
@@ -2603,13 +2624,17 @@
 
 /*
  * Logs the memory high-water mark for a process.
- * Recorded in ActivityManagerService.
+ *
+ * Pulled from StatsCompanionService for all managed processes (from ActivityManagerServie)
+ * and for selected native processes.
  */
 message ProcessMemoryHighWaterMark {
     // The uid if available. -1 means not available.
     optional int32 uid = 1 [(is_uid) = true];
 
-    // The process name. Provided by ActivityManagerService or read from /proc/PID/cmdline.
+    // The process name.
+    // Usually package name or process cmdline.
+    // Provided by ActivityManagerService or read from /proc/PID/cmdline.
     optional string process_name = 2;
 
     // RSS high-water mark. Peak RSS usage of the process. Read from the VmHWM field in
@@ -3586,4 +3611,4 @@
  */
 message DocsUIInvalidScopedAccessRequestReported {
     optional android.stats.docsui.InvalidScopedAccess type = 1;
-}
\ No newline at end of file
+}
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
index 4501b64..c8c3920 100644
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
+++ b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
@@ -19,6 +19,8 @@
 
 #include <android/hardware/power/1.0/IPower.h>
 #include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/stats/1.0/IPowerStats.h>
+
 #include <fcntl.h>
 #include <hardware/power.h>
 #include <hardware_legacy/power.h>
@@ -42,9 +44,12 @@
 using android::hardware::power::V1_0::IPower;
 using android::hardware::power::V1_0::PowerStatePlatformSleepState;
 using android::hardware::power::V1_0::PowerStateVoter;
-using android::hardware::power::V1_0::Status;
 using android::hardware::power::V1_1::PowerStateSubsystem;
 using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
+using android::hardware::power::stats::V1_0::PowerEntityInfo;
+using android::hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
+using android::hardware::power::stats::V1_0::PowerEntityStateSpace;
+
 using android::hardware::Return;
 using android::hardware::Void;
 
@@ -55,44 +60,209 @@
 namespace os {
 namespace statsd {
 
+std::function<bool(vector<shared_ptr<LogEvent>>* data)> gPuller = {};
+
 sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr;
 sp<android::hardware::power::V1_1::IPower> gPowerHalV1_1 = nullptr;
+sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0 = nullptr;
+
+std::unordered_map<uint32_t, std::string> gEntityNames = {};
+std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> gStateNames = {};
+
 std::mutex gPowerHalMutex;
-bool gPowerHalExists = true;
 
-bool getPowerHal() {
-    if (gPowerHalExists && gPowerHalV1_0 == nullptr) {
-        gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
-        if (gPowerHalV1_0 != nullptr) {
-            gPowerHalV1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
-            ALOGI("Loaded power HAL service");
-        } else {
-            ALOGW("Couldn't load power HAL service");
-            gPowerHalExists = false;
-        }
+// The caller must be holding gPowerHalMutex.
+void deinitPowerStatsLocked() {
+    gPowerHalV1_0 = nullptr;
+    gPowerHalV1_1 = nullptr;
+    gPowerStatsHalV1_0 = nullptr;
+}
+
+struct PowerHalDeathRecipient : virtual public hardware::hidl_death_recipient {
+    virtual void serviceDied(uint64_t cookie,
+        const wp<android::hidl::base::V1_0::IBase>& who) override {
+        // The HAL just died. Reset all handles to HAL services.
+        std::lock_guard<std::mutex> lock(gPowerHalMutex);
+        deinitPowerStatsLocked();
     }
-    return gPowerHalV1_0 != nullptr;
+};
+
+sp<PowerHalDeathRecipient> gDeathRecipient = new PowerHalDeathRecipient();
+
+SubsystemSleepStatePuller::SubsystemSleepStatePuller() :
+    StatsPuller(android::util::SUBSYSTEM_SLEEP_STATE) {
 }
 
-SubsystemSleepStatePuller::SubsystemSleepStatePuller() : StatsPuller(android::util::SUBSYSTEM_SLEEP_STATE) {
+// The caller must be holding gPowerHalMutex.
+bool checkResultLocked(const Return<void> &ret, const char* function) {
+    if (!ret.isOk()) {
+        ALOGE("%s failed: requested HAL service not available. Description: %s",
+            function, ret.description().c_str());
+        if (ret.isDeadObject()) {
+            deinitPowerStatsLocked();
+        }
+        return false;
+    }
+    return true;
 }
 
-bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
-    std::lock_guard<std::mutex> lock(gPowerHalMutex);
+// The caller must be holding gPowerHalMutex.
+// gPowerStatsHalV1_0 must not be null
+bool initializePowerStats() {
+    using android::hardware::power::stats::V1_0::Status;
 
-    if (!getPowerHal()) {
-        ALOGE("Power Hal not loaded");
+    // Clear out previous content if we are re-initializing
+    gEntityNames.clear();
+    gStateNames.clear();
+
+    Return<void> ret;
+    ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) {
+        if (status != Status::SUCCESS) {
+            ALOGE("Error getting power entity info");
+            return;
+        }
+
+        // construct lookup table of powerEntityId to power entity name
+        for (auto info : infos) {
+            gEntityNames.emplace(info.powerEntityId, info.powerEntityName);
+        }
+    });
+    if (!checkResultLocked(ret, __func__)) {
+        return false;
+    }
+
+    ret = gPowerStatsHalV1_0->getPowerEntityStateInfo({}, [](auto stateSpaces, auto status) {
+        if (status != Status::SUCCESS) {
+            ALOGE("Error getting state info");
+            return;
+        }
+
+        // construct lookup table of powerEntityId, powerEntityStateId to power entity state name
+        for (auto stateSpace : stateSpaces) {
+            std::unordered_map<uint32_t, std::string> stateNames = {};
+            for (auto state : stateSpace.states) {
+                stateNames.emplace(state.powerEntityStateId,
+                    state.powerEntityStateName);
+            }
+            gStateNames.emplace(stateSpace.powerEntityId, stateNames);
+        }
+    });
+    if (!checkResultLocked(ret, __func__)) {
+        return false;
+    }
+
+    return (!gEntityNames.empty()) && (!gStateNames.empty());
+}
+
+// The caller must be holding gPowerHalMutex.
+bool getPowerStatsHalLocked() {
+    if(gPowerStatsHalV1_0 == nullptr) {
+        gPowerStatsHalV1_0 = android::hardware::power::stats::V1_0::IPowerStats::getService();
+        if (gPowerStatsHalV1_0 == nullptr) {
+            ALOGE("Unable to get power.stats HAL service.");
+            return false;
+        }
+
+        // Link death recipient to power.stats service handle
+        hardware::Return<bool> linked = gPowerStatsHalV1_0->linkToDeath(gDeathRecipient, 0);
+        if (!linked.isOk()) {
+            ALOGE("Transaction error in linking to power.stats HAL death: %s",
+                    linked.description().c_str());
+            deinitPowerStatsLocked();
+            return false;
+        } else if (!linked) {
+            ALOGW("Unable to link to power.stats HAL death notifications");
+            // We should still continue even though linking failed
+        }
+        return initializePowerStats();
+    }
+    return true;
+}
+
+// The caller must be holding gPowerHalMutex.
+bool getIPowerStatsDataLocked(vector<shared_ptr<LogEvent>>* data) {
+    using android::hardware::power::stats::V1_0::Status;
+
+    if(!getPowerStatsHalLocked()) {
         return false;
     }
 
     int64_t wallClockTimestampNs = getWallClockNs();
     int64_t elapsedTimestampNs = getElapsedRealtimeNs();
 
-    data->clear();
+    // Get power entity state residency data
+    bool success = false;
+    Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData({},
+        [&data, &success, wallClockTimestampNs, elapsedTimestampNs]
+        (auto results, auto status) {
+        if (status == Status::NOT_SUPPORTED) {
+            ALOGW("getPowerEntityStateResidencyData is not supported");
+            success = false;
+            return;
+        }
 
-    Return<void> ret;
+        for(auto result : results) {
+            for(auto stateResidency : result.stateResidencyData) {
+                auto statePtr = make_shared<LogEvent>(
+                        android::util::SUBSYSTEM_SLEEP_STATE,
+                        wallClockTimestampNs, elapsedTimestampNs);
+                statePtr->write(gEntityNames.at(result.powerEntityId));
+                statePtr->write(gStateNames.at(result.powerEntityId)
+                    .at(stateResidency.powerEntityStateId));
+                statePtr->write(stateResidency.totalStateEntryCount);
+                statePtr->write(stateResidency.totalTimeInStateMs);
+                statePtr->init();
+                data->emplace_back(statePtr);
+            }
+        }
+        success = true;
+    });
+    // Intentionally not returning early here.
+    // bool success determines if this succeeded or not.
+    checkResultLocked(ret, __func__);
+
+    return success;
+}
+
+// The caller must be holding gPowerHalMutex.
+bool getPowerHalLocked() {
+    if(gPowerHalV1_0 == nullptr) {
+        gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
+        if(gPowerHalV1_0 == nullptr) {
+            ALOGE("Unable to get power HAL service.");
+            return false;
+        }
+        gPowerHalV1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
+
+        // Link death recipient to power service handle
+        hardware::Return<bool> linked = gPowerHalV1_0->linkToDeath(gDeathRecipient, 0);
+        if (!linked.isOk()) {
+            ALOGE("Transaction error in linking to power HAL death: %s",
+                    linked.description().c_str());
+            gPowerHalV1_0 = nullptr;
+            return false;
+        } else if (!linked) {
+            ALOGW("Unable to link to power. death notifications");
+            // We should still continue even though linking failed
+        }
+    }
+    return true;
+}
+
+// The caller must be holding gPowerHalMutex.
+bool getIPowerDataLocked(vector<shared_ptr<LogEvent>>* data) {
+    using android::hardware::power::V1_0::Status;
+
+    if(!getPowerHalLocked()) {
+        return false;
+    }
+
+    int64_t wallClockTimestampNs = getWallClockNs();
+    int64_t elapsedTimestampNs = getElapsedRealtimeNs();
+        Return<void> ret;
         ret = gPowerHalV1_0->getPlatformLowPowerStats(
-                [&data, wallClockTimestampNs, elapsedTimestampNs](hidl_vec<PowerStatePlatformSleepState> states, Status status) {
+                [&data, wallClockTimestampNs, elapsedTimestampNs]
+                    (hidl_vec<PowerStatePlatformSleepState> states, Status status) {
                     if (status != Status::SUCCESS) return;
 
                     for (size_t i = 0; i < states.size(); i++) {
@@ -128,9 +298,7 @@
                         }
                     }
                 });
-        if (!ret.isOk()) {
-            ALOGE("getLowPowerStats() failed: power HAL service not available");
-            gPowerHalV1_0 = nullptr;
+        if (!checkResultLocked(ret, __func__)) {
             return false;
         }
 
@@ -139,35 +307,68 @@
                 android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
         if (gPowerHal_1_1 != nullptr) {
             ret = gPowerHal_1_1->getSubsystemLowPowerStats(
-                    [&data, wallClockTimestampNs, elapsedTimestampNs](hidl_vec<PowerStateSubsystem> subsystems, Status status) {
-                        if (status != Status::SUCCESS) return;
+            [&data, wallClockTimestampNs, elapsedTimestampNs]
+            (hidl_vec<PowerStateSubsystem> subsystems, Status status) {
+                if (status != Status::SUCCESS) return;
 
-                        if (subsystems.size() > 0) {
-                            for (size_t i = 0; i < subsystems.size(); i++) {
-                                const PowerStateSubsystem& subsystem = subsystems[i];
-                                for (size_t j = 0; j < subsystem.states.size(); j++) {
-                                    const PowerStateSubsystemSleepState& state =
-                                            subsystem.states[j];
-                                    auto subsystemStatePtr = make_shared<LogEvent>(
-                                        android::util::SUBSYSTEM_SLEEP_STATE,
-                                        wallClockTimestampNs, elapsedTimestampNs);
-                                    subsystemStatePtr->write(subsystem.name);
-                                    subsystemStatePtr->write(state.name);
-                                    subsystemStatePtr->write(state.totalTransitions);
-                                    subsystemStatePtr->write(state.residencyInMsecSinceBoot);
-                                    subsystemStatePtr->init();
-                                    data->push_back(subsystemStatePtr);
-                                    VLOG("subsystemstate: %s, %s, %lld, %lld, %lld",
-                                         subsystem.name.c_str(), state.name.c_str(),
-                                         (long long)state.residencyInMsecSinceBoot,
-                                         (long long)state.totalTransitions,
-                                         (long long)state.lastEntryTimestampMs);
-                                }
-                            }
+                if (subsystems.size() > 0) {
+                    for (size_t i = 0; i < subsystems.size(); i++) {
+                        const PowerStateSubsystem& subsystem = subsystems[i];
+                        for (size_t j = 0; j < subsystem.states.size(); j++) {
+                            const PowerStateSubsystemSleepState& state =
+                                    subsystem.states[j];
+                            auto subsystemStatePtr = make_shared<LogEvent>(
+                                android::util::SUBSYSTEM_SLEEP_STATE,
+                                wallClockTimestampNs, elapsedTimestampNs);
+                            subsystemStatePtr->write(subsystem.name);
+                            subsystemStatePtr->write(state.name);
+                            subsystemStatePtr->write(state.totalTransitions);
+                            subsystemStatePtr->write(state.residencyInMsecSinceBoot);
+                            subsystemStatePtr->init();
+                            data->push_back(subsystemStatePtr);
+                            VLOG("subsystemstate: %s, %s, %lld, %lld, %lld",
+                                 subsystem.name.c_str(), state.name.c_str(),
+                                 (long long)state.residencyInMsecSinceBoot,
+                                 (long long)state.totalTransitions,
+                                 (long long)state.lastEntryTimestampMs);
                         }
-                    });
+                    }
+                }
+            });
         }
-    return true;
+        return true;
+}
+
+// The caller must be holding gPowerHalMutex.
+std::function<bool(vector<shared_ptr<LogEvent>>* data)> getPullerLocked() {
+    std::function<bool(vector<shared_ptr<LogEvent>>* data)> ret = {};
+
+    // First see if power.stats HAL is available. Fall back to power HAL if
+    // power.stats HAL is unavailable.
+    if(android::hardware::power::stats::V1_0::IPowerStats::getService() != nullptr) {
+        ALOGI("Using power.stats HAL");
+        ret = getIPowerStatsDataLocked;
+    } else if(android::hardware::power::V1_0::IPower::getService() != nullptr) {
+        ALOGI("Using power HAL");
+        ret = getIPowerDataLocked;
+    }
+
+    return ret;
+}
+
+bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
+    std::lock_guard<std::mutex> lock(gPowerHalMutex);
+
+    if(!gPuller) {
+        gPuller = getPullerLocked();
+    }
+
+    if(gPuller) {
+        return gPuller(data);
+    }
+
+    ALOGE("Unable to load Power Hal or power.stats HAL");
+    return false;
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
index 597377e..8464b8df 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/Utils.java
@@ -70,9 +70,16 @@
     /**
      * Dumps the report from the device and converts it to a ConfigMetricsReportList.
      * Erases the data if clearData is true.
+     * @param configId id of the config
+     * @param clearData whether to erase the report data from statsd after getting the report.
+     * @param useShellUid Pulls data for the {@link SHELL_UID} instead of the caller's uid.
+     * @param logger Logger to log error messages
+     * @return
+     * @throws IOException
+     * @throws InterruptedException
      */
     public static ConfigMetricsReportList getReportList(long configId, boolean clearData,
-            Logger logger) throws IOException, InterruptedException {
+            boolean useShellUid, Logger logger) throws IOException, InterruptedException {
         try {
             File outputFile = File.createTempFile("statsdret", ".bin");
             outputFile.deleteOnExit();
@@ -82,7 +89,7 @@
                     "adb",
                     "shell",
                     CMD_DUMP_REPORT,
-                    SHELL_UID,
+                    useShellUid ? SHELL_UID : "",
                     String.valueOf(configId),
                     clearData ? "" : "--keep_data",
                     "--include_current_bucket",
@@ -93,8 +100,8 @@
         } catch (com.google.protobuf.InvalidProtocolBufferException e) {
             logger.severe("Failed to fetch and parse the statsd output report. "
                             + "Perhaps there is not a valid statsd config for the requested "
-                            + "uid=" + SHELL_UID
-                            + ", configId=" + configId
+                            + (useShellUid ? ("uid=" + SHELL_UID + ", ") : "")
+                            + "configId=" + configId
                             + ".");
             throw (e);
         }
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
index 08074ed..67fb906 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/localdrive/LocalDrive.java
@@ -165,7 +165,7 @@
             try {
                 Utils.runCommand(null, sLogger, "adb", "shell", Utils.CMD_REMOVE_CONFIG,
                         Utils.SHELL_UID, String.valueOf(configId));
-                Utils.getReportList(configId, true /* clearData */, sLogger);
+                Utils.getReportList(configId, true /* clearData */, true /* SHELL_UID */, sLogger);
             } catch (InterruptedException | IOException e) {
                 sLogger.severe("Failed to remove config: " + e.getMessage());
                 return false;
@@ -234,7 +234,7 @@
         // Even if the args request no modifications, we still parse it to make sure it's valid.
         ConfigMetricsReportList reportList;
         try {
-            reportList = Utils.getReportList(configId, clearData, sLogger);
+            reportList = Utils.getReportList(configId, clearData, true /* SHELL_UID */, sLogger);
         } catch (IOException | InterruptedException e) {
             sLogger.severe("Failed to get report list: " + e.getMessage());
             return false;
@@ -278,7 +278,7 @@
         sLogger.fine(String.format("cmdClear with %d", configId));
 
         try {
-            Utils.getReportList(configId, true /* clearData */, sLogger);
+            Utils.getReportList(configId, true /* clearData */, true /* SHELL_UID */, sLogger);
         } catch (IOException | InterruptedException e) {
             sLogger.severe("Failed to get report list: " + e.getMessage());
             return false;
diff --git a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
index f7bd44a..e3fe928 100644
--- a/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
+++ b/cmds/statsd/tools/localtools/src/com/android/statsd/shelltools/testdrive/TestDrive.java
@@ -165,7 +165,7 @@
     }
 
     private void dumpMetrics() throws Exception {
-        ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, logger);
+        ConfigMetricsReportList reportList = Utils.getReportList(CONFIG_ID, true, false, logger);
         // We may get multiple reports. Take the last one.
         ConfigMetricsReport report = reportList.getReports(reportList.getReportsCount() - 1);
         // Really should be only one metric.
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index bacb991..bf14438 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -2730,8 +2730,6 @@
 Lcom/android/internal/telephony/CommandsInterface;->changeBarringPassword(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Landroid/os/Message;)V
 Lcom/android/internal/telephony/CommandsInterface;->deleteSmsOnRuim(ILandroid/os/Message;)V
 Lcom/android/internal/telephony/CommandsInterface;->deleteSmsOnSim(ILandroid/os/Message;)V
-Lcom/android/internal/telephony/CommandsInterface;->dial(Ljava/lang/String;ILandroid/os/Message;)V
-Lcom/android/internal/telephony/CommandsInterface;->dial(Ljava/lang/String;ILcom/android/internal/telephony/UUSInfo;Landroid/os/Message;)V
 Lcom/android/internal/telephony/CommandsInterface;->exitEmergencyCallbackMode(Landroid/os/Message;)V
 Lcom/android/internal/telephony/CommandsInterface;->getBasebandVersion(Landroid/os/Message;)V
 Lcom/android/internal/telephony/CommandsInterface;->getCdmaBroadcastConfig(Landroid/os/Message;)V
@@ -2861,7 +2859,6 @@
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mActiveState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcActiveState;
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mConnectionParams:Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mDataRegState:I
-Lcom/android/internal/telephony/dataconnection/DataConnection;->mDcFailCause:Lcom/android/internal/telephony/dataconnection/DcFailCause;
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mDct:Lcom/android/internal/telephony/dataconnection/DcTracker;
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectingErrorCreatingConnection:Lcom/android/internal/telephony/dataconnection/DataConnection$DcDisconnectionErrorCreatingConnection;
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mDisconnectingState:Lcom/android/internal/telephony/dataconnection/DataConnection$DcDisconnectingState;
@@ -2872,10 +2869,8 @@
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mNetworkInfo:Landroid/net/NetworkInfo;
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mPhone:Lcom/android/internal/telephony/Phone;
 Lcom/android/internal/telephony/dataconnection/DataConnection;->mRilRat:I
-Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyAllDisconnectCompleted(Lcom/android/internal/telephony/dataconnection/DcFailCause;)V
 Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyAllOfConnected(Ljava/lang/String;)V
 Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyAllOfDisconnectDcRetrying(Ljava/lang/String;)V
-Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyConnectCompleted(Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;Lcom/android/internal/telephony/dataconnection/DcFailCause;Z)V
 Lcom/android/internal/telephony/dataconnection/DataConnection;->notifyDisconnectCompleted(Lcom/android/internal/telephony/dataconnection/DataConnection$DisconnectParams;Z)V
 Lcom/android/internal/telephony/dataconnection/DataConnection;->onConnect(Lcom/android/internal/telephony/dataconnection/DataConnection$ConnectionParams;)V
 Lcom/android/internal/telephony/dataconnection/DataConnection;->tearDownData(Ljava/lang/Object;)V
@@ -2884,22 +2879,6 @@
 Lcom/android/internal/telephony/dataconnection/DcController;->mDcListActiveByCid:Ljava/util/HashMap;
 Lcom/android/internal/telephony/dataconnection/DcController;->mDct:Lcom/android/internal/telephony/dataconnection/DcTracker;
 Lcom/android/internal/telephony/dataconnection/DcController;->mDcTesterDeactivateAll:Lcom/android/internal/telephony/dataconnection/DcTesterDeactivateAll;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->ACTIVATION_REJECT_GGSN:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->ACTIVATION_REJECT_UNSPECIFIED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->APN_TYPE_CONFLICT:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->INSUFFICIENT_RESOURCES:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->MISSING_UNKNOWN_APN:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->NSAPI_IN_USE:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->ONLY_IPV4_ALLOWED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->ONLY_IPV6_ALLOWED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->ONLY_SINGLE_BEARER_ALLOWED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->OPERATOR_BARRED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->PROTOCOL_ERRORS:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->SERVICE_OPTION_NOT_SUBSCRIBED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->SERVICE_OPTION_NOT_SUPPORTED:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->SERVICE_OPTION_OUT_OF_ORDER:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->UNKNOWN_PDP_ADDRESS_TYPE:Lcom/android/internal/telephony/dataconnection/DcFailCause;
-Lcom/android/internal/telephony/dataconnection/DcFailCause;->USER_AUTHENTICATION:Lcom/android/internal/telephony/dataconnection/DcFailCause;
 Lcom/android/internal/telephony/dataconnection/DcTracker$RecoveryAction;->isAggressiveRecovery(I)Z
 Lcom/android/internal/telephony/dataconnection/DcTracker;->cancelReconnectAlarm(Lcom/android/internal/telephony/dataconnection/ApnContext;)V
 Lcom/android/internal/telephony/dataconnection/DcTracker;->cleanUpAllConnections(Ljava/lang/String;)V
@@ -3763,8 +3742,6 @@
 Lcom/android/internal/telephony/TelephonyProperties;->PROPERTY_ICC_OPERATOR_NUMERIC:Ljava/lang/String;
 Lcom/android/internal/telephony/test/InterpreterEx;-><init>(Ljava/lang/String;)V
 Lcom/android/internal/telephony/test/SimulatedCommands;->acceptCall(Landroid/os/Message;)V
-Lcom/android/internal/telephony/test/SimulatedCommands;->dial(Ljava/lang/String;ILandroid/os/Message;)V
-Lcom/android/internal/telephony/test/SimulatedCommands;->dial(Ljava/lang/String;ILcom/android/internal/telephony/UUSInfo;Landroid/os/Message;)V
 Lcom/android/internal/telephony/test/SimulatedCommands;->mDcSuccess:Z
 Lcom/android/internal/telephony/test/SimulatedCommands;->resultFail(Landroid/os/Message;Ljava/lang/Object;Ljava/lang/Throwable;)V
 Lcom/android/internal/telephony/test/SimulatedCommands;->resultSuccess(Landroid/os/Message;Ljava/lang/Object;)V
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 5e445d14..b584d5d 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2545,7 +2545,7 @@
      * picture-in-picture.
      *
      * @return true if the system successfully put this activity into picture-in-picture mode or was
-     * already in picture-in-picture mode (@see {@link #isInPictureInPictureMode()). If the device
+     * already in picture-in-picture mode (see {@link #isInPictureInPictureMode()}). If the device
      * does not support picture-in-picture, return false.
      */
     public boolean enterPictureInPictureMode(@NonNull PictureInPictureParams params) {
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index f928501..b42d53a 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -306,6 +306,6 @@
     public abstract void setDebugFlagsForStartingActivity(ActivityInfo aInfo, int startFlags,
             ProfilerInfo profilerInfo, Object wmLock);
 
-    /** Checks if process running with given pid has access to full external storage or not */
-    public abstract boolean isAppStorageSandboxed(int pid, int uid);
+    /** Returns mount mode for process running with given pid */
+    public abstract int getStorageMountMode(int pid, int uid);
 }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 67d9ad6e..2b81c86 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -44,6 +44,7 @@
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.IntentFilterVerificationInfo;
 import android.content.pm.KeySet;
+import android.content.pm.ModuleInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageItemInfo;
@@ -791,6 +792,16 @@
         throw new NameNotFoundException("No shared userid for user:"+sharedUserName);
     }
 
+    @Override
+    public List<ModuleInfo> getInstalledModules(int flags) {
+        return null;
+    }
+
+    @Override
+    public ModuleInfo getModuleInfo(String packageName, int flags) throws NameNotFoundException {
+        return null;
+    }
+
     @SuppressWarnings("unchecked")
     @Override
     public List<PackageInfo> getInstalledPackages(int flags) {
diff --git a/core/java/android/app/RemoteInput.java b/core/java/android/app/RemoteInput.java
index ebbf317..392921e 100644
--- a/core/java/android/app/RemoteInput.java
+++ b/core/java/android/app/RemoteInput.java
@@ -173,7 +173,7 @@
 
     /**
      * Returns true if the input only accepts data, meaning {@link #getAllowFreeFormInput}
-     * is false, {@link #getChoices} is null or empty, and {@link #getAllowedDataTypes is
+     * is false, {@link #getChoices} is null or empty, and {@link #getAllowedDataTypes} is
      * non-null and not empty.
      */
     public boolean isDataOnly() {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 43e1836..f4fd5d1 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -154,7 +154,7 @@
 import android.telephony.TelephonyManager;
 import android.telephony.euicc.EuiccCardManager;
 import android.telephony.euicc.EuiccManager;
-import android.telephony.rcs.RcsManager;
+import android.telephony.ims.RcsManager;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
diff --git a/core/java/android/app/VrManager.java b/core/java/android/app/VrManager.java
index 6248e7c..5b87de4 100644
--- a/core/java/android/app/VrManager.java
+++ b/core/java/android/app/VrManager.java
@@ -5,7 +5,6 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -222,7 +221,6 @@
      * @param componentName ComponentName of a VR InputMethod that should be set as selected
      * input by InputMethodManagerService.
      */
-    @TestApi
     @RequiresPermission(android.Manifest.permission.RESTRICTED_VR_ACCESS)
     public void setVrInputMethod(ComponentName componentName) {
         try {
diff --git a/core/java/android/app/WaitResult.java b/core/java/android/app/WaitResult.java
index 5baf2e2..ad9f680 100644
--- a/core/java/android/app/WaitResult.java
+++ b/core/java/android/app/WaitResult.java
@@ -16,11 +16,14 @@
 
 package android.app;
 
+import android.annotation.IntDef;
 import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 
 /**
  * Information returned after waiting for an activity start.
@@ -28,11 +31,43 @@
  * @hide
  */
 public class WaitResult implements Parcelable {
+
+    /**
+     * The state at which a launch sequence had started.
+     *
+     * @see <a href="https://developer.android.com/topic/performance/vitals/launch-time"App startup
+     * time</a>
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"LAUNCH_STATE_"}, value = {
+            LAUNCH_STATE_COLD,
+            LAUNCH_STATE_WARM,
+            LAUNCH_STATE_HOT
+    })
+    public @interface LaunchState {
+    }
+
+    /**
+     * Cold launch sequence: a new process has started.
+     */
+    public static final int LAUNCH_STATE_COLD = 1;
+
+    /**
+     * Warm launch sequence: process reused, but activity has to be created.
+     */
+    public static final int LAUNCH_STATE_WARM = 2;
+
+    /**
+     * Hot launch sequence: process reused, activity brought-to-top.
+     */
+    public static final int LAUNCH_STATE_HOT = 3;
+
     public static final int INVALID_DELAY = -1;
     public int result;
     public boolean timeout;
     public ComponentName who;
     public long totalTime;
+    public @LaunchState int launchState;
 
     public WaitResult() {
     }
@@ -48,6 +83,7 @@
         dest.writeInt(timeout ? 1 : 0);
         ComponentName.writeToParcel(who, dest);
         dest.writeLong(totalTime);
+        dest.writeInt(launchState);
     }
 
     public static final Parcelable.Creator<WaitResult> CREATOR
@@ -68,6 +104,7 @@
         timeout = source.readInt() != 0;
         who = ComponentName.readFromParcel(source);
         totalTime = source.readLong();
+        launchState = source.readInt();
     }
 
     public void dump(PrintWriter pw, String prefix) {
@@ -76,5 +113,19 @@
         pw.println(prefix + "  timeout=" + timeout);
         pw.println(prefix + "  who=" + who);
         pw.println(prefix + "  totalTime=" + totalTime);
+        pw.println(prefix + "  launchState=" + launchState);
+    }
+
+    public static String launchStateToString(@LaunchState int type) {
+        switch (type) {
+            case LAUNCH_STATE_COLD:
+                return "COLD";
+            case LAUNCH_STATE_WARM:
+                return "WARM";
+            case LAUNCH_STATE_HOT:
+                return "HOT";
+            default:
+                return "UNKNOWN (" + type + ")";
+        }
     }
 }
\ No newline at end of file
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index ca60e14..0ccd49f 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -209,7 +209,7 @@
      *
      * @param sliceUri Uri to bind.
      * @param supportedSpecs List of supported specs.
-     * @see Slice.
+     * @see Slice
      * @see Slice#HINT_PARTIAL
      */
     public Slice onBindSlice(Uri sliceUri, Set<SliceSpec> supportedSpecs) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 8e6a385..87b64797 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -643,6 +643,7 @@
     private final IBluetoothManager mManagerService;
     @UnsupportedAppUsage
     private IBluetooth mService;
+    private Context mContext;
     private final ReentrantReadWriteLock mServiceLock = new ReentrantReadWriteLock();
 
     private final Object mLock = new Object();
@@ -1541,6 +1542,23 @@
     }
 
     /**
+     * Set the context for this BluetoothAdapter (only called from BluetoothManager)
+     * @hide
+     */
+    public void setContext(Context context) {
+        mContext = context;
+    }
+
+    private String getOpPackageName() {
+        // Workaround for legacy API for getting a BluetoothAdapter not
+        // passing a context
+        if (mContext != null) {
+            return mContext.getOpPackageName();
+        }
+        return ActivityThread.currentOpPackageName();
+    }
+
+    /**
      * Start the remote device discovery process.
      * <p>The discovery process usually involves an inquiry scan of about 12
      * seconds, followed by a page scan of each new device to retrieve its
@@ -1577,7 +1595,7 @@
         try {
             mServiceLock.readLock().lock();
             if (mService != null) {
-                return mService.startDiscovery();
+                return mService.startDiscovery(getOpPackageName());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
index e3672a7..e08d405 100644
--- a/core/java/android/bluetooth/BluetoothManager.java
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -67,6 +67,7 @@
         }
         // Legacy api - getDefaultAdapter does not take in the context
         mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mAdapter.setContext(context);
     }
 
     /**
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 5a12e4e..f138d39 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -327,6 +327,7 @@
         public ContentProviderResult[] applyBatch(String callingPkg, String authority,
                 ArrayList<ContentProviderOperation> operations)
                 throws OperationApplicationException {
+            validateIncomingAuthority(authority);
             int numOperations = operations.size();
             final int[] userIds = new int[numOperations];
             for (int i = 0; i < numOperations; i++) {
@@ -447,6 +448,7 @@
         @Override
         public Bundle call(String callingPkg, String authority, String method, @Nullable String arg,
                 @Nullable Bundle extras) {
+            validateIncomingAuthority(authority);
             Bundle.setDefusable(extras, true);
             Trace.traceBegin(TRACE_TAG_DATABASE, "call");
             final String original = setCallingPackage(callingPkg);
@@ -1182,12 +1184,12 @@
      * Implement this to handle query requests where the arguments are packed into a {@link Bundle}.
      * Arguments may include traditional SQL style query arguments. When present these
      * should be handled  according to the contract established in
-     * {@link #query(Uri, String[], String, String[], String, CancellationSignal).
+     * {@link #query(Uri, String[], String, String[], String, CancellationSignal)}.
      *
      * <p>Traditional SQL arguments can be found in the bundle using the following keys:
-     * <li>{@link ContentResolver#QUERY_ARG_SQL_SELECTION}
-     * <li>{@link ContentResolver#QUERY_ARG_SQL_SELECTION_ARGS}
-     * <li>{@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER}
+     * <li>{@link android.content.ContentResolver#QUERY_ARG_SQL_SELECTION}
+     * <li>{@link android.content.ContentResolver#QUERY_ARG_SQL_SELECTION_ARGS}
+     * <li>{@link android.content.ContentResolver#QUERY_ARG_SQL_SORT_ORDER}
      *
      * <p>This method can be called from multiple threads, as described in
      * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
@@ -1244,8 +1246,8 @@
 
         return cursor;</pre>
      * <p>
-     * @see #query(Uri, String[], String, String[], String, CancellationSignal) for
-     *     implementation details.
+     * See {@link #query(Uri, String[], String, String[], String, CancellationSignal)}
+     * for implementation details.
      *
      * @param uri The URI to query. This will be the full URI sent by the client.
      * @param projection The list of columns to put into the cursor.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index d7d3cb5..b39010d 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4462,7 +4462,7 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve an
-     * {@link android.telephony.rcs.RcsManager}.
+     * {@link android.telephony.ims.RcsManager}.
      * @hide
      */
     public static final String TELEPHONY_RCS_SERVICE = "ircs";
diff --git a/core/java/android/content/pm/ModuleInfo.java b/core/java/android/content/pm/ModuleInfo.java
new file mode 100644
index 0000000..07e640b
--- /dev/null
+++ b/core/java/android/content/pm/ModuleInfo.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2018 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.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Information you can retrieve about a particular system
+ * module.
+ */
+public final class ModuleInfo implements Parcelable {
+
+     // NOTE: When adding new data members be sure to update the copy-constructor, Parcel
+     // constructor, and writeToParcel.
+
+    /** Public name of this module. */
+    private String mName;
+
+    /** The package name of this module. */
+    private String mPackageName;
+
+    /** Whether or not this module is hidden from the user. */
+    private boolean mHidden;
+
+    // TODO: Decide whether we need an additional metadata bundle to support out of band
+    // updates to ModuleInfo.
+    //
+    // private Bundle mMetadata;
+
+    /** @hide */
+    public ModuleInfo() {
+    }
+
+    /** @hide */
+    public ModuleInfo(ModuleInfo orig) {
+        mName = orig.mName;
+        mPackageName = orig.mPackageName;
+        mHidden = orig.mHidden;
+    }
+
+    /** @hide Sets the public name of this module. */
+    public ModuleInfo setName(String name) {
+        mName = name;
+        return this;
+    }
+
+    /** Gets the public name of this module. */
+    public @Nullable String getName() {
+        return mName;
+    }
+
+    /** @hide Sets the package name of this module. */
+    public ModuleInfo setPackageName(String packageName) {
+        mPackageName = packageName;
+        return this;
+    }
+
+    /** Gets the package name of this module. */
+    public @Nullable String getPackageName() {
+        return mPackageName;
+    }
+
+    /** @hide Sets whether or not this package is hidden. */
+    public ModuleInfo setHidden(boolean hidden) {
+        mHidden = hidden;
+        return this;
+    }
+
+    /** Gets whether or not this package is hidden. */
+    public boolean isHidden() {
+        return mHidden;
+    }
+
+    /** Returns a string representation of this object. */
+    public String toString() {
+        return "ModuleInfo{"
+            + Integer.toHexString(System.identityHashCode(this))
+            + " " + mName + "}";
+    }
+
+    /** Describes the kinds of special objects contained in this object. */
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        int hashCode = 0;
+        hashCode = 31 * hashCode + Objects.hashCode(mName);
+        hashCode = 31 * hashCode + Objects.hashCode(mPackageName);
+        hashCode = 31 * hashCode + Boolean.hashCode(mHidden);
+        return hashCode;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof ModuleInfo)) {
+            return false;
+        }
+        final ModuleInfo other = (ModuleInfo) obj;
+        return Objects.equals(mName, other.mName)
+                && Objects.equals(mPackageName, other.mPackageName)
+                && mHidden == other.mHidden;
+    }
+
+    /** Flattens this object into the given {@link Parcel}. */
+    public void writeToParcel(Parcel dest, int parcelableFlags) {
+        dest.writeString(mName);
+        dest.writeString(mPackageName);
+        dest.writeBoolean(mHidden);
+    }
+
+    private ModuleInfo(Parcel source) {
+        mName = source.readString();
+        mPackageName = source.readString();
+        mHidden = source.readBoolean();
+    }
+
+    public static final Parcelable.Creator<ModuleInfo> CREATOR =
+            new Parcelable.Creator<ModuleInfo>() {
+        public ModuleInfo createFromParcel(Parcel source) {
+            return new ModuleInfo(source);
+        }
+        public ModuleInfo[] newArray(int size) {
+            return new ModuleInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 34cdfee..566017b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -220,6 +220,12 @@
 
     /** @hide */
     @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ModuleInfoFlags {}
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "GET_", "MATCH_" }, value = {
             GET_META_DATA,
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -3467,6 +3473,35 @@
             @ComponentInfoFlags int flags) throws NameNotFoundException;
 
     /**
+     * Retrieve information for a particular module.
+     *
+     * @param packageName The name of the module.
+     * @param flags Additional option flags to modify the data returned.
+     * @return A {@link ModuleInfo} object containing information about the
+     *         module.
+     * @throws NameNotFoundException if a module with the given name cannot be
+     *             found on the system.
+     */
+    public ModuleInfo getModuleInfo(String packageName, @ModuleInfoFlags int flags)
+            throws NameNotFoundException {
+        throw new UnsupportedOperationException(
+                "getModuleInfo not implemented in subclass");
+    }
+
+    /**
+     * Return a List of all modules that are installed.
+     *
+     * @param flags Additional option flags to modify the data returned.
+     * @return A {@link List} of {@link ModuleInfo} objects, one for each installed
+     *         module, containing information about the module. In the unlikely case
+     *         there are no installed modules, an empty list is returned.
+     */
+    public @NonNull List<ModuleInfo> getInstalledModules(@ModuleInfoFlags int flags) {
+        throw new UnsupportedOperationException(
+                "getInstalledModules not implemented in subclass");
+    }
+
+    /**
      * Return a List of all packages that are installed for the current user.
      *
      * @param flags Additional option flags to modify the data returned.
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index c437dde..eae1aa5 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -2283,7 +2283,8 @@
     static final String[] DATA_CONNECTION_NAMES = {
         "none", "gprs", "edge", "umts", "cdma", "evdo_0", "evdo_A",
         "1xrtt", "hsdpa", "hsupa", "hspa", "iden", "evdo_b", "lte",
-        "ehrpd", "hspap", "gsm", "td_scdma", "iwlan", "lte_ca", "other"
+        "ehrpd", "hspap", "gsm", "td_scdma", "iwlan", "lte_ca", "nr",
+        "other"
     };
 
     public static final int NUM_DATA_CONNECTION_TYPES = DATA_CONNECTION_OTHER+1;
@@ -4730,7 +4731,7 @@
             sb.append("\n       ");
             sb.append(prefix);
             didOne = true;
-            sb.append(DATA_CONNECTION_NAMES[i]);
+            sb.append(i < DATA_CONNECTION_NAMES.length ? DATA_CONNECTION_NAMES[i] : "ERROR");
             sb.append(" ");
             formatTimeMs(sb, time/1000);
             sb.append("(");
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 17ce79b..0a60764 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1515,11 +1515,11 @@
      * background user; the result here does not distinguish between the two.
      *
      * <p>Note prior to Android Nougat MR1 (SDK version <= 24;
-     * {@link android.os.Build.VERSION_CODES#N), this API required a system permission
+     * {@link android.os.Build.VERSION_CODES#N}, this API required a system permission
      * in order to check other profile's status.
      * Since Android Nougat MR1 (SDK version >= 25;
-     * {@link android.os.Build.VERSION_CODES#N_MR1)), the restriction has been relaxed, and now
-     * it'll accept any {@link UserHandle} within the same profile group as the caller.
+     * {@link android.os.Build.VERSION_CODES#N_MR1}), the restriction has been relaxed, and now
+     * it'll accept any {@link android.os.UserHandle} within the same profile group as the caller.
      *
      * @param user The user to retrieve the running state for.
      */
@@ -1544,11 +1544,11 @@
      * (but is not yet fully stopped, and still running some code).
      *
      * <p>Note prior to Android Nougat MR1 (SDK version <= 24;
-     * {@link android.os.Build.VERSION_CODES#N), this API required a system permission
+     * {@link android.os.Build.VERSION_CODES#N}, this API required a system permission
      * in order to check other profile's status.
      * Since Android Nougat MR1 (SDK version >= 25;
-     * {@link android.os.Build.VERSION_CODES#N_MR1)), the restriction has been relaxed, and now
-     * it'll accept any {@link UserHandle} within the same profile group as the caller.
+     * {@link android.os.Build.VERSION_CODES#N_MR1}), the restriction has been relaxed, and now
+     * it'll accept any {@link android.os.UserHandle} within the same profile group as the caller.
      *
      * @param user The user to retrieve the running state for.
      */
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 7fd0a4b..f136cd6 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -398,6 +398,8 @@
             argsForZygote.add("--mount-external-write");
         } else if (mountExternal == Zygote.MOUNT_EXTERNAL_FULL) {
             argsForZygote.add("--mount-external-full");
+        } else if (mountExternal == Zygote.MOUNT_EXTERNAL_INSTALLER) {
+            argsForZygote.add("--mount-external-installer");
         }
 
         argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index a8726e9..cd991cc 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -1275,7 +1275,7 @@
         }
     }
 
-    /** @removed */
+    @Deprecated
     public static Bitmap getDocumentThumbnail(ContentResolver content, Uri documentUri, Point size,
             CancellationSignal signal) throws FileNotFoundException {
         return getDocumentThumbnail((ContentInterface) content, documentUri, size, signal);
@@ -1307,7 +1307,7 @@
         }
     }
 
-    /** @removed */
+    @Deprecated
     public static Uri createDocument(ContentResolver content, Uri parentDocumentUri,
             String mimeType, String displayName) throws FileNotFoundException {
         return createDocument((ContentInterface) content, parentDocumentUri, mimeType, displayName);
@@ -1345,7 +1345,7 @@
         }
     }
 
-    /** @removed */
+    @Deprecated
     public static boolean isChildDocument(ContentResolver content, Uri parentDocumentUri,
             Uri childDocumentUri) throws FileNotFoundException {
         return isChildDocument((ContentInterface) content, parentDocumentUri, childDocumentUri);
@@ -1382,7 +1382,7 @@
         }
     }
 
-    /** @removed */
+    @Deprecated
     public static Uri renameDocument(ContentResolver content, Uri documentUri,
             String displayName) throws FileNotFoundException {
         return renameDocument((ContentInterface) content, documentUri, displayName);
@@ -1410,7 +1410,7 @@
         }
     }
 
-    /** @removed */
+    @Deprecated
     public static boolean deleteDocument(ContentResolver content, Uri documentUri)
             throws FileNotFoundException {
         return deleteDocument((ContentInterface) content, documentUri);
@@ -1441,7 +1441,7 @@
         }
     }
 
-    /** @removed */
+    @Deprecated
     public static Uri copyDocument(ContentResolver content, Uri sourceDocumentUri,
             Uri targetParentDocumentUri) throws FileNotFoundException {
         return copyDocument((ContentInterface) content, sourceDocumentUri, targetParentDocumentUri);
@@ -1474,7 +1474,7 @@
         }
     }
 
-    /** @removed */
+    @Deprecated
     public static Uri moveDocument(ContentResolver content, Uri sourceDocumentUri,
             Uri sourceParentDocumentUri, Uri targetParentDocumentUri) throws FileNotFoundException {
         return moveDocument((ContentInterface) content, sourceDocumentUri, sourceParentDocumentUri,
@@ -1508,7 +1508,7 @@
         }
     }
 
-    /** @removed */
+    @Deprecated
     public static boolean removeDocument(ContentResolver content, Uri documentUri,
             Uri parentDocumentUri) throws FileNotFoundException {
         return removeDocument((ContentInterface) content, documentUri, parentDocumentUri);
@@ -1531,7 +1531,7 @@
         }
     }
 
-    /** @removed */
+    @Deprecated
     public static void ejectRoot(ContentResolver content, Uri rootUri) {
         ejectRoot((ContentInterface) content, rootUri);
     }
@@ -1581,7 +1581,7 @@
         }
     }
 
-    /** @removed */
+    @Deprecated
     public static Bundle getDocumentMetadata(ContentResolver content, Uri documentUri)
             throws FileNotFoundException {
         return getDocumentMetadata((ContentInterface) content, documentUri);
@@ -1618,7 +1618,7 @@
         }
     }
 
-    /** @removed */
+    @Deprecated
     public static Path findDocumentPath(ContentResolver content, Uri treeUri)
             throws FileNotFoundException {
         return findDocumentPath((ContentInterface) content, treeUri);
@@ -1697,7 +1697,7 @@
         }
     }
 
-    /** @removed */
+    @Deprecated
     public static IntentSender createWebLinkIntent(ContentResolver content, Uri uri,
             Bundle options) throws FileNotFoundException {
         return createWebLinkIntent((ContentInterface) content, uri, options);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 93a5950..cbcc492 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7867,6 +7867,24 @@
         public static final String ASSIST_GESTURE_SETUP_COMPLETE = "assist_gesture_setup_complete";
 
         /**
+         * Control whether Trust Agents are in active unlock or extend unlock mode.
+         * @hide
+         */
+        public static final String TRUST_AGENTS_EXTEND_UNLOCK = "trust_agents_extend_unlock";
+
+        private static final Validator TRUST_AGENTS_EXTEND_UNLOCK_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
+        /**
+         * Control whether the screen locks when trust is lost.
+         * @hide
+         */
+        public static final String LOCK_SCREEN_WHEN_TRUST_LOST = "lock_screen_when_trust_lost";
+
+        private static final Validator LOCK_SCREEN_WHEN_TRUST_LOST_VALIDATOR =
+                BOOLEAN_VALIDATOR;
+
+        /**
          * Control whether Night display is currently activated.
          * @hide
          */
@@ -8382,6 +8400,8 @@
             ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS,
             ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS,
             NOTIFICATION_NEW_INTERRUPTION_MODEL,
+            TRUST_AGENTS_EXTEND_UNLOCK,
+            LOCK_SCREEN_WHEN_TRUST_LOST,
         };
 
         /**
@@ -8543,6 +8563,8 @@
             VALIDATORS.put(USER_SETUP_COMPLETE, BOOLEAN_VALIDATOR);
             VALIDATORS.put(ASSIST_GESTURE_SETUP_COMPLETE, BOOLEAN_VALIDATOR);
             VALIDATORS.put(NOTIFICATION_NEW_INTERRUPTION_MODEL, BOOLEAN_VALIDATOR);
+            VALIDATORS.put(TRUST_AGENTS_EXTEND_UNLOCK, TRUST_AGENTS_EXTEND_UNLOCK_VALIDATOR);
+            VALIDATORS.put(LOCK_SCREEN_WHEN_TRUST_LOST, LOCK_SCREEN_WHEN_TRUST_LOST_VALIDATOR);
         }
 
         /**
diff --git a/core/java/android/text/style/ImageSpan.java b/core/java/android/text/style/ImageSpan.java
index d4edde9..13ac9ff 100644
--- a/core/java/android/text/style/ImageSpan.java
+++ b/core/java/android/text/style/ImageSpan.java
@@ -259,7 +259,8 @@
      * Returns the source string that was saved during construction.
      *
      * @return the source string that was saved during construction
-     * @see #ImageSpan(Drawable, String) and this{@link #ImageSpan(Context, Uri)}
+     * @see #ImageSpan(Drawable, String)
+     * @see #ImageSpan(Context, Uri)
      */
     @Nullable
     public String getSource() {
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index b59d8c7..a86abe5 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -2588,6 +2588,38 @@
     }
 
     /**
+     * Returns the original raw X coordinate of this event.  For touch
+     * events on the screen, this is the original location of the event
+     * on the screen, before it had been adjusted for the containing window
+     * and views.
+     *
+     * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
+     * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     *
+     * @see #getX(int)
+     * @see #AXIS_X
+     */
+    public float getRawX(int pointerIndex) {
+        return nativeGetRawAxisValue(mNativePtr, AXIS_X, pointerIndex, HISTORY_CURRENT);
+    }
+
+    /**
+     * Returns the original raw Y coordinate of this event.  For touch
+     * events on the screen, this is the original location of the event
+     * on the screen, before it had been adjusted for the containing window
+     * and views.
+     *
+     * @param pointerIndex Raw index of pointer to retrieve.  Value may be from 0
+     * (the first pointer that is down) to {@link #getPointerCount()}-1.
+     *
+     * @see #getY(int)
+     * @see #AXIS_Y
+     */
+    public float getRawY(int pointerIndex) {
+        return nativeGetRawAxisValue(mNativePtr, AXIS_Y, pointerIndex, HISTORY_CURRENT);
+    }
+
+    /**
      * Return the precision of the X coordinates being reported.  You can
      * multiply this number with {@link #getX} to find the actual hardware
      * value of the X coordinate.
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index 1889692..cc0264a 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -31,7 +31,9 @@
 import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.util.Log;
+import android.util.TimeUtils;
 import android.view.View;
 import android.view.ViewStructure;
 import android.view.autofill.AutofillId;
@@ -98,6 +100,12 @@
      */
     public static final int STATE_DISABLED = 3;
 
+    /**
+     * Handler message used to flush the buffer.
+     */
+    private static final int MSG_FLUSH = 1;
+
+
     private static final String BG_THREAD_NAME = "intel_svc_streamer_thread";
 
     /**
@@ -106,6 +114,12 @@
     // TODO(b/111276913): use settings
     private static final int MAX_BUFFER_SIZE = 100;
 
+    /**
+     * Frequency the buffer is flushed if stale.
+     */
+    // TODO(b/111276913): use settings
+    private static final int FLUSHING_FREQUENCY_MS = 5_000;
+
     @NonNull
     private final AtomicBoolean mDisabled = new AtomicBoolean();
 
@@ -136,6 +150,9 @@
     // held at the Application level
     private final Handler mHandler;
 
+    // Used just for debugging purposes (on dump)
+    private long mNextFlush;
+
     /** @hide */
     public ContentCaptureManager(@NonNull Context context,
             @Nullable IContentCaptureManager service) {
@@ -207,9 +224,17 @@
             mEvents = new ArrayList<>(MAX_BUFFER_SIZE);
         }
         mEvents.add(event);
+
         final int numberEvents = mEvents.size();
-        if (numberEvents < MAX_BUFFER_SIZE && !forceFlush) {
-            // Buffering events, return right away...
+
+        // TODO(b/120784831): need to optimize it so we buffer changes until a number of X are
+        // buffered (either total or per autofillid). For
+        // example, if the user typed "a", "b", "c" and the threshold is 3, we should buffer
+        // "a" and "b" then send "abc".
+        final boolean bufferEvent = numberEvents < MAX_BUFFER_SIZE;
+
+        if (bufferEvent && !forceFlush) {
+            handleScheduleFlush();
             return;
         }
 
@@ -236,10 +261,38 @@
             return;
         }
 
+        handleForceFlush();
+    }
+
+    private void handleScheduleFlush() {
+        if (mHandler.hasMessages(MSG_FLUSH)) {
+            // "Renew" the flush message by removing the previous one
+            mHandler.removeMessages(MSG_FLUSH);
+        }
+        mNextFlush = SystemClock.elapsedRealtime() + FLUSHING_FREQUENCY_MS;
+        if (VERBOSE) {
+            Log.v(TAG, "Scheduled to flush in " + FLUSHING_FREQUENCY_MS + "ms: " + mNextFlush);
+        }
+        mHandler.sendMessageDelayed(
+                obtainMessage(ContentCaptureManager::handleFlushIfNeeded, this).setWhat(MSG_FLUSH),
+                FLUSHING_FREQUENCY_MS);
+    }
+
+    private void handleFlushIfNeeded() {
+        if (mEvents.isEmpty()) {
+            if (VERBOSE) Log.v(TAG, "Nothing to flush");
+            return;
+        }
+        handleForceFlush();
+    }
+
+    private void handleForceFlush() {
+        final int numberEvents = mEvents.size();
         try {
             if (DEBUG) {
                 Log.d(TAG, "Flushing " + numberEvents + " event(s) for " + getActivityDebugName());
             }
+            mHandler.removeMessages(MSG_FLUSH);
             mService.sendEvents(mContext.getUserId(), mId, mEvents);
             // TODO(b/111276913): decide whether we should clear or set it to null, as each has
             // its own advantages: clearing will save extra allocations while the session is
@@ -307,6 +360,7 @@
         mApplicationToken = null;
         mComponentName = null;
         mEvents = null;
+        mHandler.removeMessages(MSG_FLUSH);
     }
 
     /**
@@ -443,7 +497,7 @@
             pw.print(prefix2); pw.print("component name: ");
             pw.println(mComponentName.flattenToShortString());
         }
-        if (mEvents != null) {
+        if (mEvents != null && !mEvents.isEmpty()) {
             final int numberEvents = mEvents.size();
             pw.print(prefix2); pw.print("buffered events: "); pw.print(numberEvents);
             pw.print('/'); pw.println(MAX_BUFFER_SIZE);
@@ -455,6 +509,9 @@
                     pw.println();
                 }
             }
+            pw.print(prefix2); pw.print("flush frequency: "); pw.println(FLUSHING_FREQUENCY_MS);
+            pw.print(prefix2); pw.print("next flush: ");
+            TimeUtils.formatDuration(mNextFlush - SystemClock.elapsedRealtime(), pw); pw.println();
         }
     }
 
diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java
index 3842f66..4da3391 100644
--- a/core/java/com/android/internal/app/procstats/AssociationState.java
+++ b/core/java/com/android/internal/app/procstats/AssociationState.java
@@ -445,8 +445,18 @@
         }
     }
 
+    public boolean hasProcess(String procName) {
+        final int NSRC = mSources.size();
+        for (int isrc = 0; isrc < NSRC; isrc++) {
+            if (mSources.keyAt(isrc).mProcess.equals(procName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix,
-            long now, long totalTime, boolean dumpDetails, boolean dumpAll) {
+            long now, long totalTime, String reqPackage, boolean dumpDetails, boolean dumpAll) {
         if (dumpAll) {
             pw.print(prefix);
             pw.print("mNumActive=");
@@ -456,6 +466,9 @@
         for (int isrc = 0; isrc < NSRC; isrc++) {
             final SourceKey key = mSources.keyAt(isrc);
             final SourceState src = mSources.valueAt(isrc);
+            if (reqPackage != null && !reqPackage.equals(key.mProcess)) {
+                continue;
+            }
             pw.print(prefixInner);
             pw.print("<- ");
             pw.print(key.mProcess);
diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java
index 19d8a83..9ee583a 100644
--- a/core/java/com/android/internal/app/procstats/ProcessStats.java
+++ b/core/java/com/android/internal/app/procstats/ProcessStats.java
@@ -1396,10 +1396,10 @@
         return as;
     }
 
-    // See b/118826162 -- to avoid logspaming, we rate limit the WTF.
-    private static final long INVERSE_PROC_STATE_WTF_MIN_INTERVAL_MS = 10_000L;
-    private long mNextInverseProcStateWtfUptime;
-    private int mSkippedInverseProcStateWtfCount;
+    // See b/118826162 -- to avoid logspaming, we rate limit the warnings.
+    private static final long INVERSE_PROC_STATE_WARNING_MIN_INTERVAL_MS = 10_000L;
+    private long mNextInverseProcStateWarningUptime;
+    private int mSkippedInverseProcStateWarningCount;
 
     public void updateTrackingAssociationsLocked(int curSeq, long now) {
         final int NUM = mTrackingAssociations.size();
@@ -1423,18 +1423,19 @@
                         act.stopActive(now);
                         if (act.mProcState < procState) {
                             final long nowUptime = SystemClock.uptimeMillis();
-                            if (mNextInverseProcStateWtfUptime > nowUptime) {
-                                mSkippedInverseProcStateWtfCount++;
+                            if (mNextInverseProcStateWarningUptime > nowUptime) {
+                                mSkippedInverseProcStateWarningCount++;
                             } else {
                                 // TODO We still see it during boot related to GMS-core.
                                 // b/118826162
-                                Slog.wtf(TAG, "Tracking association " + act + " whose proc state "
+                                Slog.w(TAG, "Tracking association " + act + " whose proc state "
                                         + act.mProcState + " is better than process " + proc
                                         + " proc state " + procState
-                                        + " (" +  mSkippedInverseProcStateWtfCount + " skipped)");
-                                mSkippedInverseProcStateWtfCount = 0;
-                                mNextInverseProcStateWtfUptime =
-                                        nowUptime + INVERSE_PROC_STATE_WTF_MIN_INTERVAL_MS;
+                                        + " (" +  mSkippedInverseProcStateWarningCount
+                                        + " skipped)");
+                                mSkippedInverseProcStateWarningCount = 0;
+                                mNextInverseProcStateWarningUptime =
+                                        nowUptime + INVERSE_PROC_STATE_WARNING_MIN_INTERVAL_MS;
                             }
                         }
                     }
@@ -1474,6 +1475,7 @@
                         final int NSRVS = pkgState.mServices.size();
                         final int NASCS = pkgState.mAssociations.size();
                         final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName);
+                        boolean onlyAssociations = false;
                         if (!pkgMatch) {
                             boolean procMatch = false;
                             for (int iproc = 0; iproc < NPROCS; iproc++) {
@@ -1484,7 +1486,18 @@
                                 }
                             }
                             if (!procMatch) {
-                                continue;
+                                // Check if this app has any associations with the requested
+                                // package, so that if so we print those.
+                                for (int iasc = 0; iasc < NASCS; iasc++) {
+                                    AssociationState asc = pkgState.mAssociations.valueAt(iasc);
+                                    if (asc.hasProcess(reqPackage)) {
+                                        onlyAssociations = true;
+                                        break;
+                                    }
+                                }
+                                if (!onlyAssociations) {
+                                    continue;
+                                }
                             }
                         }
                         if (NPROCS > 0 || NSRVS > 0 || NASCS > 0) {
@@ -1502,7 +1515,7 @@
                             pw.print(vers);
                             pw.println(":");
                         }
-                        if ((section & REPORT_PKG_PROC_STATS) != 0) {
+                        if ((section & REPORT_PKG_PROC_STATS) != 0 && !onlyAssociations) {
                             if (!dumpSummary || dumpAll) {
                                 for (int iproc = 0; iproc < NPROCS; iproc++) {
                                     ProcessState proc = pkgState.mProcesses.valueAt(iproc);
@@ -1549,7 +1562,7 @@
                                         now, totalTime);
                             }
                         }
-                        if ((section & REPORT_PKG_SVC_STATS) != 0) {
+                        if ((section & REPORT_PKG_SVC_STATS) != 0 && !onlyAssociations) {
                             for (int isvc = 0; isvc < NSRVS; isvc++) {
                                 ServiceState svc = pkgState.mServices.valueAt(isvc);
                                 if (!pkgMatch && !reqPackage.equals(svc.getProcessName())) {
@@ -1578,7 +1591,9 @@
                             for (int iasc = 0; iasc < NASCS; iasc++) {
                                 AssociationState asc = pkgState.mAssociations.valueAt(iasc);
                                 if (!pkgMatch && !reqPackage.equals(asc.getProcessName())) {
-                                    continue;
+                                    if (!onlyAssociations || !asc.hasProcess(reqPackage)) {
+                                        continue;
+                                    }
                                 }
                                 if (activeOnly && !asc.isInUse()) {
                                     pw.print("      (Not active association: ");
@@ -1596,7 +1611,8 @@
                                 pw.print("        Process: ");
                                 pw.println(asc.getProcessName());
                                 asc.dumpStats(pw, "        ", "          ", "    ",
-                                        now, totalTime, dumpDetails, dumpAll);
+                                        now, totalTime, onlyAssociations ? reqPackage : null,
+                                        dumpDetails, dumpAll);
                             }
                         }
                     }
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 65213c0..65b9fad 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -81,6 +81,11 @@
     public static final int MOUNT_EXTERNAL_READ = IVold.REMOUNT_MODE_READ;
     /** Read-write external storage should be mounted. */
     public static final int MOUNT_EXTERNAL_WRITE = IVold.REMOUNT_MODE_WRITE;
+    /**
+     * Mount mode for package installers which should give them access to
+     * all obb dirs in addition to their package sandboxes
+     */
+    public static final int MOUNT_EXTERNAL_INSTALLER = IVold.REMOUNT_MODE_INSTALLER;
     /** Read-write external storage should be mounted instead of package sandbox */
     public static final int MOUNT_EXTERNAL_FULL = IVold.REMOUNT_MODE_FULL;
 
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 4a94ec4..f182c4d 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -656,7 +656,9 @@
                     mountExternal = Zygote.MOUNT_EXTERNAL_WRITE;
                 } else if (arg.equals("--mount-external-full")) {
                     mountExternal = Zygote.MOUNT_EXTERNAL_FULL;
-                }  else if (arg.equals("--query-abi-list")) {
+                }  else if (arg.equals("--mount-external-installer")) {
+                    mountExternal = Zygote.MOUNT_EXTERNAL_INSTALLER;
+                } else if (arg.equals("--query-abi-list")) {
                     abiListQuery = true;
                 } else if (arg.equals("--get-pid")) {
                     pidQuery = true;
diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java
index 151901b..470533f2 100644
--- a/core/java/com/android/internal/util/CollectionUtils.java
+++ b/core/java/com/android/internal/util/CollectionUtils.java
@@ -326,4 +326,8 @@
             throw ExceptionUtils.propagate(e);
         }
     }
+
+    public static @NonNull <T> List<T> defeatNullable(@Nullable List<T> val) {
+        return (val != null) ? val : Collections.emptyList();
+    }
 }
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 2e674a5..841e5b6 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -67,6 +67,7 @@
     private static final int ALLOW_PRIVAPP_PERMISSIONS = 0x10;
     private static final int ALLOW_OEM_PERMISSIONS = 0x20;
     private static final int ALLOW_HIDDENAPI_WHITELISTING = 0x40;
+    private static final int ALLOW_ASSOCIATIONS = 0x80;
     private static final int ALLOW_ALL = ~0;
 
     // property for runtime configuration differentiation
@@ -195,6 +196,12 @@
 
     final ArrayMap<String, ArrayMap<String, Boolean>> mOemPermissions = new ArrayMap<>();
 
+    // Allowed associations between applications.  If there are any entries
+    // for an app, those are the only associations allowed; otherwise, all associations
+    // are allowed.  Allowing an association from app A to app B means app A can not
+    // associate with any other apps, but does not limit what apps B can associate with.
+    final ArrayMap<String, ArraySet<String>> mAllowedAssociations = new ArrayMap<>();
+
     public static SystemConfig getInstance() {
         synchronized (SystemConfig.class) {
             if (sInstance == null) {
@@ -320,6 +327,10 @@
         return Collections.emptyMap();
     }
 
+    public ArrayMap<String, ArraySet<String>> getAllowedAssociations() {
+        return mAllowedAssociations;
+    }
+
     SystemConfig() {
         // Read configuration from system
         readPermissions(Environment.buildPath(
@@ -329,8 +340,9 @@
         readPermissions(Environment.buildPath(
                 Environment.getRootDirectory(), "etc", "permissions"), ALLOW_ALL);
 
-        // Vendors are only allowed to customze libs, features and privapp permissions
-        int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS;
+        // Vendors are only allowed to customize these
+        int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS
+                | ALLOW_ASSOCIATIONS;
         if (Build.VERSION.FIRST_SDK_INT <= Build.VERSION_CODES.O_MR1) {
             // For backward compatibility
             vendorPermissionFlag |= (ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS);
@@ -359,8 +371,8 @@
                     odmPermissionFlag);
         }
 
-        // Allow OEM to customize features and OEM permissions
-        int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS;
+        // Allow OEM to customize these
+        int oemPermissionFlag = ALLOW_FEATURES | ALLOW_OEM_PERMISSIONS | ALLOW_ASSOCIATIONS;
         readPermissions(Environment.buildPath(
                 Environment.getOemDirectory(), "etc", "sysconfig"), oemPermissionFlag);
         readPermissions(Environment.buildPath(
@@ -423,6 +435,11 @@
         }
     }
 
+    private void logNotAllowedInPartition(String name, File permFile, XmlPullParser parser) {
+        Slog.w(TAG, "<" + name + "> not allowed in partition of "
+                + permFile + " at " + parser.getPositionDescription());
+    }
+
     private void readPermissionsFromXml(File permFile, int permissionFlag) {
         FileReader permReader = null;
         try {
@@ -453,14 +470,17 @@
                         + ": found " + parser.getName() + ", expected 'permissions' or 'config'");
             }
 
-            boolean allowAll = permissionFlag == ALLOW_ALL;
-            boolean allowLibs = (permissionFlag & ALLOW_LIBS) != 0;
-            boolean allowFeatures = (permissionFlag & ALLOW_FEATURES) != 0;
-            boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0;
-            boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;
-            boolean allowPrivappPermissions = (permissionFlag & ALLOW_PRIVAPP_PERMISSIONS) != 0;
-            boolean allowOemPermissions = (permissionFlag & ALLOW_OEM_PERMISSIONS) != 0;
-            boolean allowApiWhitelisting = (permissionFlag & ALLOW_HIDDENAPI_WHITELISTING) != 0;
+            final boolean allowAll = permissionFlag == ALLOW_ALL;
+            final boolean allowLibs = (permissionFlag & ALLOW_LIBS) != 0;
+            final boolean allowFeatures = (permissionFlag & ALLOW_FEATURES) != 0;
+            final boolean allowPermissions = (permissionFlag & ALLOW_PERMISSIONS) != 0;
+            final boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;
+            final boolean allowPrivappPermissions = (permissionFlag & ALLOW_PRIVAPP_PERMISSIONS)
+                    != 0;
+            final boolean allowOemPermissions = (permissionFlag & ALLOW_OEM_PERMISSIONS) != 0;
+            final boolean allowApiWhitelisting = (permissionFlag & ALLOW_HIDDENAPI_WHITELISTING)
+                    != 0;
+            final boolean allowAssociations = (permissionFlag & ALLOW_ASSOCIATIONS) != 0;
             while (true) {
                 XmlUtils.nextElement(parser);
                 if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
@@ -468,297 +488,425 @@
                 }
 
                 String name = parser.getName();
-                if ("group".equals(name) && allowAll) {
-                    String gidStr = parser.getAttributeValue(null, "gid");
-                    if (gidStr != null) {
-                        int gid = android.os.Process.getGidForName(gidStr);
-                        mGlobalGids = appendInt(mGlobalGids, gid);
-                    } else {
-                        Slog.w(TAG, "<group> without gid in " + permFile + " at "
-                                + parser.getPositionDescription());
-                    }
-
+                if (name == null) {
                     XmlUtils.skipCurrentTag(parser);
                     continue;
-                } else if ("permission".equals(name) && allowPermissions) {
-                    String perm = parser.getAttributeValue(null, "name");
-                    if (perm == null) {
-                        Slog.w(TAG, "<permission> without name in " + permFile + " at "
-                                + parser.getPositionDescription());
-                        XmlUtils.skipCurrentTag(parser);
-                        continue;
-                    }
-                    perm = perm.intern();
-                    readPermission(parser, perm);
-
-                } else if ("assign-permission".equals(name) && allowPermissions) {
-                    String perm = parser.getAttributeValue(null, "name");
-                    if (perm == null) {
-                        Slog.w(TAG, "<assign-permission> without name in " + permFile + " at "
-                                + parser.getPositionDescription());
-                        XmlUtils.skipCurrentTag(parser);
-                        continue;
-                    }
-                    String uidStr = parser.getAttributeValue(null, "uid");
-                    if (uidStr == null) {
-                        Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at "
-                                + parser.getPositionDescription());
-                        XmlUtils.skipCurrentTag(parser);
-                        continue;
-                    }
-                    int uid = Process.getUidForName(uidStr);
-                    if (uid < 0) {
-                        Slog.w(TAG, "<assign-permission> with unknown uid \""
-                                + uidStr + "  in " + permFile + " at "
-                                + parser.getPositionDescription());
-                        XmlUtils.skipCurrentTag(parser);
-                        continue;
-                    }
-                    perm = perm.intern();
-                    ArraySet<String> perms = mSystemPermissions.get(uid);
-                    if (perms == null) {
-                        perms = new ArraySet<String>();
-                        mSystemPermissions.put(uid, perms);
-                    }
-                    perms.add(perm);
-                    XmlUtils.skipCurrentTag(parser);
-
-                } else if ("split-permission".equals(name) && allowPermissions) {
-                    readSplitPermission(parser, permFile);
-                } else if ("library".equals(name) && allowLibs) {
-                    String lname = parser.getAttributeValue(null, "name");
-                    String lfile = parser.getAttributeValue(null, "file");
-                    String ldependency = parser.getAttributeValue(null, "dependency");
-                    if (lname == null) {
-                        Slog.w(TAG, "<library> without name in " + permFile + " at "
-                                + parser.getPositionDescription());
-                    } else if (lfile == null) {
-                        Slog.w(TAG, "<library> without file in " + permFile + " at "
-                                + parser.getPositionDescription());
-                    } else {
-                        //Log.i(TAG, "Got library " + lname + " in " + lfile);
-                        SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile,
-                                ldependency == null ? new String[0] : ldependency.split(":"));
-                        mSharedLibraries.put(lname, entry);
-                    }
-                    XmlUtils.skipCurrentTag(parser);
-                    continue;
-                } else if ("feature".equals(name) && allowFeatures) {
-                    String fname = parser.getAttributeValue(null, "name");
-                    int fversion = XmlUtils.readIntAttribute(parser, "version", 0);
-                    boolean allowed;
-                    if (!lowRam) {
-                        allowed = true;
-                    } else {
-                        String notLowRam = parser.getAttributeValue(null, "notLowRam");
-                        allowed = !"true".equals(notLowRam);
-                    }
-                    if (fname == null) {
-                        Slog.w(TAG, "<feature> without name in " + permFile + " at "
-                                + parser.getPositionDescription());
-                    } else if (allowed) {
-                        addFeature(fname, fversion);
-                    }
-                    XmlUtils.skipCurrentTag(parser);
-                    continue;
-
-                } else if ("unavailable-feature".equals(name) && allowFeatures) {
-                    String fname = parser.getAttributeValue(null, "name");
-                    if (fname == null) {
-                        Slog.w(TAG, "<unavailable-feature> without name in " + permFile + " at "
-                                + parser.getPositionDescription());
-                    } else {
-                        mUnavailableFeatures.add(fname);
-                    }
-                    XmlUtils.skipCurrentTag(parser);
-                    continue;
-
-                } else if ("allow-in-power-save-except-idle".equals(name) && allowAll) {
-                    String pkgname = parser.getAttributeValue(null, "package");
-                    if (pkgname == null) {
-                        Slog.w(TAG, "<allow-in-power-save-except-idle> without package in "
-                                + permFile + " at " + parser.getPositionDescription());
-                    } else {
-                        mAllowInPowerSaveExceptIdle.add(pkgname);
-                    }
-                    XmlUtils.skipCurrentTag(parser);
-                    continue;
-
-                } else if ("allow-in-power-save".equals(name) && allowAll) {
-                    String pkgname = parser.getAttributeValue(null, "package");
-                    if (pkgname == null) {
-                        Slog.w(TAG, "<allow-in-power-save> without package in " + permFile + " at "
-                                + parser.getPositionDescription());
-                    } else {
-                        mAllowInPowerSave.add(pkgname);
-                    }
-                    XmlUtils.skipCurrentTag(parser);
-                    continue;
-
-                } else if ("allow-in-data-usage-save".equals(name) && allowAll) {
-                    String pkgname = parser.getAttributeValue(null, "package");
-                    if (pkgname == null) {
-                        Slog.w(TAG, "<allow-in-data-usage-save> without package in " + permFile
-                                + " at " + parser.getPositionDescription());
-                    } else {
-                        mAllowInDataUsageSave.add(pkgname);
-                    }
-                    XmlUtils.skipCurrentTag(parser);
-                    continue;
-
-                } else if ("allow-unthrottled-location".equals(name) && allowAll) {
-                    String pkgname = parser.getAttributeValue(null, "package");
-                    if (pkgname == null) {
-                        Slog.w(TAG, "<allow-unthrottled-location> without package in "
-                            + permFile + " at " + parser.getPositionDescription());
-                    } else {
-                        mAllowUnthrottledLocation.add(pkgname);
-                    }
-                    XmlUtils.skipCurrentTag(parser);
-                    continue;
-
-                } else if ("allow-implicit-broadcast".equals(name) && allowAll) {
-                    String action = parser.getAttributeValue(null, "action");
-                    if (action == null) {
-                        Slog.w(TAG, "<allow-implicit-broadcast> without action in " + permFile
-                                + " at " + parser.getPositionDescription());
-                    } else {
-                        mAllowImplicitBroadcasts.add(action);
-                    }
-                    XmlUtils.skipCurrentTag(parser);
-                    continue;
-
-                } else if ("app-link".equals(name) && allowAppConfigs) {
-                    String pkgname = parser.getAttributeValue(null, "package");
-                    if (pkgname == null) {
-                        Slog.w(TAG, "<app-link> without package in " + permFile + " at "
-                                + parser.getPositionDescription());
-                    } else {
-                        mLinkedApps.add(pkgname);
-                    }
-                    XmlUtils.skipCurrentTag(parser);
-                } else if ("system-user-whitelisted-app".equals(name) && allowAppConfigs) {
-                    String pkgname = parser.getAttributeValue(null, "package");
-                    if (pkgname == null) {
-                        Slog.w(TAG, "<system-user-whitelisted-app> without package in " + permFile
-                                + " at " + parser.getPositionDescription());
-                    } else {
-                        mSystemUserWhitelistedApps.add(pkgname);
-                    }
-                    XmlUtils.skipCurrentTag(parser);
-                } else if ("system-user-blacklisted-app".equals(name) && allowAppConfigs) {
-                    String pkgname = parser.getAttributeValue(null, "package");
-                    if (pkgname == null) {
-                        Slog.w(TAG, "<system-user-blacklisted-app without package in " + permFile
-                                + " at " + parser.getPositionDescription());
-                    } else {
-                        mSystemUserBlacklistedApps.add(pkgname);
-                    }
-                    XmlUtils.skipCurrentTag(parser);
-                } else if ("default-enabled-vr-app".equals(name) && allowAppConfigs) {
-                    String pkgname = parser.getAttributeValue(null, "package");
-                    String clsname = parser.getAttributeValue(null, "class");
-                    if (pkgname == null) {
-                        Slog.w(TAG, "<default-enabled-vr-app without package in " + permFile
-                                + " at " + parser.getPositionDescription());
-                    } else if (clsname == null) {
-                        Slog.w(TAG, "<default-enabled-vr-app without class in " + permFile
-                                + " at " + parser.getPositionDescription());
-                    } else {
-                        mDefaultVrComponents.add(new ComponentName(pkgname, clsname));
-                    }
-                    XmlUtils.skipCurrentTag(parser);
-                } else if ("backup-transport-whitelisted-service".equals(name) && allowFeatures) {
-                    String serviceName = parser.getAttributeValue(null, "service");
-                    if (serviceName == null) {
-                        Slog.w(TAG, "<backup-transport-whitelisted-service> without service in "
-                                + permFile + " at " + parser.getPositionDescription());
-                    } else {
-                        ComponentName cn = ComponentName.unflattenFromString(serviceName);
-                        if (cn == null) {
-                            Slog.w(TAG,
-                                    "<backup-transport-whitelisted-service> with invalid service name "
-                                    + serviceName + " in "+ permFile
-                                    + " at " + parser.getPositionDescription());
-                        } else {
-                            mBackupTransportWhitelist.add(cn);
-                        }
-                    }
-                    XmlUtils.skipCurrentTag(parser);
-                } else if ("disabled-until-used-preinstalled-carrier-associated-app".equals(name)
-                        && allowAppConfigs) {
-                    String pkgname = parser.getAttributeValue(null, "package");
-                    String carrierPkgname = parser.getAttributeValue(null, "carrierAppPackage");
-                    if (pkgname == null || carrierPkgname == null) {
-                        Slog.w(TAG, "<disabled-until-used-preinstalled-carrier-associated-app"
-                                + " without package or carrierAppPackage in " + permFile + " at "
-                                + parser.getPositionDescription());
-                    } else {
-                        List<String> associatedPkgs =
-                                mDisabledUntilUsedPreinstalledCarrierAssociatedApps.get(
-                                        carrierPkgname);
-                        if (associatedPkgs == null) {
-                            associatedPkgs = new ArrayList<>();
-                            mDisabledUntilUsedPreinstalledCarrierAssociatedApps.put(
-                                    carrierPkgname, associatedPkgs);
-                        }
-                        associatedPkgs.add(pkgname);
-                    }
-                    XmlUtils.skipCurrentTag(parser);
-                } else if ("disabled-until-used-preinstalled-carrier-app".equals(name)
-                        && allowAppConfigs) {
-                    String pkgname = parser.getAttributeValue(null, "package");
-                    if (pkgname == null) {
-                        Slog.w(TAG,
-                                "<disabled-until-used-preinstalled-carrier-app> without "
-                                        + "package in " + permFile + " at "
+                }
+                switch (name) {
+                    case "group": {
+                        if (allowAll) {
+                            String gidStr = parser.getAttributeValue(null, "gid");
+                            if (gidStr != null) {
+                                int gid = android.os.Process.getGidForName(gidStr);
+                                mGlobalGids = appendInt(mGlobalGids, gid);
+                            } else {
+                                Slog.w(TAG, "<" + name + "> without gid in " + permFile + " at "
                                         + parser.getPositionDescription());
-                    } else {
-                        mDisabledUntilUsedPreinstalledCarrierApps.add(pkgname);
-                    }
-                    XmlUtils.skipCurrentTag(parser);
-                } else if ("privapp-permissions".equals(name) && allowPrivappPermissions) {
-                    // privapp permissions from system, vendor, product and product_services
-                    // partitions are stored separately. This is to prevent xml files in the vendor
-                    // partition from granting permissions to priv apps in the system partition and
-                    // vice versa.
-                    boolean vendor = permFile.toPath().startsWith(
-                            Environment.getVendorDirectory().toPath() + "/")
-                            || permFile.toPath().startsWith(
-                                Environment.getOdmDirectory().toPath() + "/");
-                    boolean product = permFile.toPath().startsWith(
-                            Environment.getProductDirectory().toPath() + "/");
-                    boolean productServices = permFile.toPath().startsWith(
-                            Environment.getProductServicesDirectory().toPath() + "/");
-                    if (vendor) {
-                        readPrivAppPermissions(parser, mVendorPrivAppPermissions,
-                                mVendorPrivAppDenyPermissions);
-                    } else if (product) {
-                        readPrivAppPermissions(parser, mProductPrivAppPermissions,
-                                mProductPrivAppDenyPermissions);
-                    } else if (productServices) {
-                        readPrivAppPermissions(parser, mProductServicesPrivAppPermissions,
-                                mProductServicesPrivAppDenyPermissions);
-                    } else {
-                        readPrivAppPermissions(parser, mPrivAppPermissions,
-                                mPrivAppDenyPermissions);
-                    }
-                } else if ("oem-permissions".equals(name) && allowOemPermissions) {
-                    readOemPermissions(parser);
-                } else if ("hidden-api-whitelisted-app".equals(name) && allowApiWhitelisting) {
-                    String pkgname = parser.getAttributeValue(null, "package");
-                    if (pkgname == null) {
-                        Slog.w(TAG, "<hidden-api-whitelisted-app> without package in " + permFile
-                                + " at " + parser.getPositionDescription());
-                    } else {
-                        mHiddenApiPackageWhitelist.add(pkgname);
-                    }
-                    XmlUtils.skipCurrentTag(parser);
-                } else {
-                    Slog.w(TAG, "Tag " + name + " is unknown or not allowed in "
-                            + permFile.getParent());
-                    XmlUtils.skipCurrentTag(parser);
-                    continue;
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    case "permission": {
+                        if (allowPermissions) {
+                            String perm = parser.getAttributeValue(null, "name");
+                            if (perm == null) {
+                                Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
+                                        + parser.getPositionDescription());
+                                XmlUtils.skipCurrentTag(parser);
+                                break;
+                            }
+                            perm = perm.intern();
+                            readPermission(parser, perm);
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                            XmlUtils.skipCurrentTag(parser);
+                        }
+                    } break;
+                    case "assign-permission": {
+                        if (allowPermissions) {
+                            String perm = parser.getAttributeValue(null, "name");
+                            if (perm == null) {
+                                Slog.w(TAG, "<" + name + "> without name in " + permFile
+                                        + " at " + parser.getPositionDescription());
+                                XmlUtils.skipCurrentTag(parser);
+                                break;
+                            }
+                            String uidStr = parser.getAttributeValue(null, "uid");
+                            if (uidStr == null) {
+                                Slog.w(TAG, "<" + name + "> without uid in " + permFile
+                                        + " at " + parser.getPositionDescription());
+                                XmlUtils.skipCurrentTag(parser);
+                                break;
+                            }
+                            int uid = Process.getUidForName(uidStr);
+                            if (uid < 0) {
+                                Slog.w(TAG, "<" + name + "> with unknown uid \""
+                                        + uidStr + "  in " + permFile + " at "
+                                        + parser.getPositionDescription());
+                                XmlUtils.skipCurrentTag(parser);
+                                break;
+                            }
+                            perm = perm.intern();
+                            ArraySet<String> perms = mSystemPermissions.get(uid);
+                            if (perms == null) {
+                                perms = new ArraySet<String>();
+                                mSystemPermissions.put(uid, perms);
+                            }
+                            perms.add(perm);
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    case "split-permission": {
+                        if (allowPermissions) {
+                            readSplitPermission(parser, permFile);
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                            XmlUtils.skipCurrentTag(parser);
+                        }
+                    } break;
+                    case "library": {
+                        if (allowLibs) {
+                            String lname = parser.getAttributeValue(null, "name");
+                            String lfile = parser.getAttributeValue(null, "file");
+                            String ldependency = parser.getAttributeValue(null, "dependency");
+                            if (lname == null) {
+                                Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
+                                        + parser.getPositionDescription());
+                            } else if (lfile == null) {
+                                Slog.w(TAG, "<" + name + "> without file in " + permFile + " at "
+                                        + parser.getPositionDescription());
+                            } else {
+                                //Log.i(TAG, "Got library " + lname + " in " + lfile);
+                                SharedLibraryEntry entry = new SharedLibraryEntry(lname, lfile,
+                                        ldependency == null ? new String[0] : ldependency.split(":"));
+                                mSharedLibraries.put(lname, entry);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    case "feature": {
+                        if (allowFeatures) {
+                            String fname = parser.getAttributeValue(null, "name");
+                            int fversion = XmlUtils.readIntAttribute(parser, "version", 0);
+                            boolean allowed;
+                            if (!lowRam) {
+                                allowed = true;
+                            } else {
+                                String notLowRam = parser.getAttributeValue(null, "notLowRam");
+                                allowed = !"true".equals(notLowRam);
+                            }
+                            if (fname == null) {
+                                Slog.w(TAG, "<" + name + "> without name in " + permFile + " at "
+                                        + parser.getPositionDescription());
+                            } else if (allowed) {
+                                addFeature(fname, fversion);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    case "unavailable-feature": {
+                        if (allowFeatures) {
+                            String fname = parser.getAttributeValue(null, "name");
+                            if (fname == null) {
+                                Slog.w(TAG, "<" + name + "> without name in " + permFile
+                                        + " at " + parser.getPositionDescription());
+                            } else {
+                                mUnavailableFeatures.add(fname);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    case "allow-in-power-save-except-idle": {
+                        if (allowAll) {
+                            String pkgname = parser.getAttributeValue(null, "package");
+                            if (pkgname == null) {
+                                Slog.w(TAG, "<" + name + "> without package in "
+                                        + permFile + " at " + parser.getPositionDescription());
+                            } else {
+                                mAllowInPowerSaveExceptIdle.add(pkgname);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    case "allow-in-power-save": {
+                        if (allowAll) {
+                            String pkgname = parser.getAttributeValue(null, "package");
+                            if (pkgname == null) {
+                                Slog.w(TAG, "<" + name + "> without package in "
+                                        + permFile + " at " + parser.getPositionDescription());
+                            } else {
+                                mAllowInPowerSave.add(pkgname);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    case "allow-in-data-usage-save": {
+                        if (allowAll) {
+                            String pkgname = parser.getAttributeValue(null, "package");
+                            if (pkgname == null) {
+                                Slog.w(TAG, "<" + name + "> without package in "
+                                        + permFile + " at " + parser.getPositionDescription());
+                            } else {
+                                mAllowInDataUsageSave.add(pkgname);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    case "allow-unthrottled-location": {
+                        if (allowAll) {
+                            String pkgname = parser.getAttributeValue(null, "package");
+                            if (pkgname == null) {
+                                Slog.w(TAG, "<" + name + "> without package in "
+                                        + permFile + " at " + parser.getPositionDescription());
+                            } else {
+                                mAllowUnthrottledLocation.add(pkgname);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    case "allow-implicit-broadcast": {
+                        if (allowAll) {
+                            String action = parser.getAttributeValue(null, "action");
+                            if (action == null) {
+                                Slog.w(TAG, "<" + name + "> without action in "
+                                        + permFile + " at " + parser.getPositionDescription());
+                            } else {
+                                mAllowImplicitBroadcasts.add(action);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    case "app-link": {
+                        if (allowAppConfigs) {
+                            String pkgname = parser.getAttributeValue(null, "package");
+                            if (pkgname == null) {
+                                Slog.w(TAG, "<" + name + "> without package in " + permFile
+                                        + " at " + parser.getPositionDescription());
+                            } else {
+                                mLinkedApps.add(pkgname);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    case "system-user-whitelisted-app": {
+                        if (allowAppConfigs) {
+                            String pkgname = parser.getAttributeValue(null, "package");
+                            if (pkgname == null) {
+                                Slog.w(TAG, "<" + name + "> without package in "
+                                        + permFile + " at " + parser.getPositionDescription());
+                            } else {
+                                mSystemUserWhitelistedApps.add(pkgname);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    case "system-user-blacklisted-app": {
+                        if (allowAppConfigs) {
+                            String pkgname = parser.getAttributeValue(null, "package");
+                            if (pkgname == null) {
+                                Slog.w(TAG, "<" + name + "> without package in "
+                                        + permFile + " at " + parser.getPositionDescription());
+                            } else {
+                                mSystemUserBlacklistedApps.add(pkgname);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    case "default-enabled-vr-app": {
+                        if (allowAppConfigs) {
+                            String pkgname = parser.getAttributeValue(null, "package");
+                            String clsname = parser.getAttributeValue(null, "class");
+                            if (pkgname == null) {
+                                Slog.w(TAG, "<" + name + "> without package in "
+                                        + permFile + " at " + parser.getPositionDescription());
+                            } else if (clsname == null) {
+                                Slog.w(TAG, "<" + name + "> without class in "
+                                        + permFile + " at " + parser.getPositionDescription());
+                            } else {
+                                mDefaultVrComponents.add(new ComponentName(pkgname, clsname));
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    case "backup-transport-whitelisted-service": {
+                        if (allowFeatures) {
+                            String serviceName = parser.getAttributeValue(null, "service");
+                            if (serviceName == null) {
+                                Slog.w(TAG, "<" + name + "> without service in "
+                                        + permFile + " at " + parser.getPositionDescription());
+                            } else {
+                                ComponentName cn = ComponentName.unflattenFromString(serviceName);
+                                if (cn == null) {
+                                    Slog.w(TAG, "<" + name + "> with invalid service name "
+                                            + serviceName + " in " + permFile
+                                            + " at " + parser.getPositionDescription());
+                                } else {
+                                    mBackupTransportWhitelist.add(cn);
+                                }
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    case "disabled-until-used-preinstalled-carrier-associated-app": {
+                        if (allowAppConfigs) {
+                            String pkgname = parser.getAttributeValue(null, "package");
+                            String carrierPkgname = parser.getAttributeValue(null,
+                                    "carrierAppPackage");
+                            if (pkgname == null || carrierPkgname == null) {
+                                Slog.w(TAG, "<" + name
+                                        + "> without package or carrierAppPackage in " + permFile
+                                        + " at " + parser.getPositionDescription());
+                            } else {
+                                List<String> associatedPkgs =
+                                        mDisabledUntilUsedPreinstalledCarrierAssociatedApps.get(
+                                                carrierPkgname);
+                                if (associatedPkgs == null) {
+                                    associatedPkgs = new ArrayList<>();
+                                    mDisabledUntilUsedPreinstalledCarrierAssociatedApps.put(
+                                            carrierPkgname, associatedPkgs);
+                                }
+                                associatedPkgs.add(pkgname);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    case "disabled-until-used-preinstalled-carrier-app": {
+                        if (allowAppConfigs) {
+                            String pkgname = parser.getAttributeValue(null, "package");
+                            if (pkgname == null) {
+                                Slog.w(TAG,
+                                        "<" + name + "> without "
+                                                + "package in " + permFile + " at "
+                                                + parser.getPositionDescription());
+                            } else {
+                                mDisabledUntilUsedPreinstalledCarrierApps.add(pkgname);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    case "privapp-permissions": {
+                        if (allowPrivappPermissions) {
+                            // privapp permissions from system, vendor, product and product_services
+                            // partitions are stored separately. This is to prevent xml files in
+                            // the vendor partition from granting permissions to priv apps in the
+                            // system partition and vice versa.
+                            boolean vendor = permFile.toPath().startsWith(
+                                    Environment.getVendorDirectory().toPath() + "/")
+                                    || permFile.toPath().startsWith(
+                                    Environment.getOdmDirectory().toPath() + "/");
+                            boolean product = permFile.toPath().startsWith(
+                                    Environment.getProductDirectory().toPath() + "/");
+                            boolean productServices = permFile.toPath().startsWith(
+                                    Environment.getProductServicesDirectory().toPath() + "/");
+                            if (vendor) {
+                                readPrivAppPermissions(parser, mVendorPrivAppPermissions,
+                                        mVendorPrivAppDenyPermissions);
+                            } else if (product) {
+                                readPrivAppPermissions(parser, mProductPrivAppPermissions,
+                                        mProductPrivAppDenyPermissions);
+                            } else if (productServices) {
+                                readPrivAppPermissions(parser, mProductServicesPrivAppPermissions,
+                                        mProductServicesPrivAppDenyPermissions);
+                            } else {
+                                readPrivAppPermissions(parser, mPrivAppPermissions,
+                                        mPrivAppDenyPermissions);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                            XmlUtils.skipCurrentTag(parser);
+                        }
+                    } break;
+                    case "oem-permissions": {
+                        if (allowOemPermissions) {
+                            readOemPermissions(parser);
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                            XmlUtils.skipCurrentTag(parser);
+                        }
+                    } break;
+                    case "hidden-api-whitelisted-app": {
+                        if (allowApiWhitelisting) {
+                            String pkgname = parser.getAttributeValue(null, "package");
+                            if (pkgname == null) {
+                                Slog.w(TAG, "<" + name + "> without package in "
+                                        + permFile + " at " + parser.getPositionDescription());
+                            } else {
+                                mHiddenApiPackageWhitelist.add(pkgname);
+                            }
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    case "allow-association": {
+                        if (allowAssociations) {
+                            String target = parser.getAttributeValue(null, "target");
+                            if (target == null) {
+                                Slog.w(TAG, "<" + name + "> without target in " + permFile
+                                        + " at " + parser.getPositionDescription());
+                                XmlUtils.skipCurrentTag(parser);
+                                break;
+                            }
+                            String allowed = parser.getAttributeValue(null, "allowed");
+                            if (allowed == null) {
+                                Slog.w(TAG, "<" + name + "> without allowed in " + permFile
+                                        + " at " + parser.getPositionDescription());
+                                XmlUtils.skipCurrentTag(parser);
+                                break;
+                            }
+                            target = target.intern();
+                            allowed = allowed.intern();
+                            ArraySet<String> associations = mAllowedAssociations.get(target);
+                            if (associations == null) {
+                                associations = new ArraySet<>();
+                                mAllowedAssociations.put(target, associations);
+                            }
+                            Slog.i(TAG, "Adding association: " + target + " <- " + allowed);
+                            associations.add(allowed);
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
+                    default: {
+                        Slog.w(TAG, "Tag " + name + " is unknown in "
+                                + permFile + " at " + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
                 }
             }
         } catch (XmlPullParserException e) {
diff --git a/core/jni/android/graphics/ColorFilter.cpp b/core/jni/android/graphics/ColorFilter.cpp
index 6ebf35c..a54571b 100644
--- a/core/jni/android/graphics/ColorFilter.cpp
+++ b/core/jni/android/graphics/ColorFilter.cpp
@@ -36,7 +36,7 @@
         return static_cast<jlong>(reinterpret_cast<uintptr_t>(&SafeUnref));
     }
 
-    static jlong CreatePorterDuffFilter(JNIEnv* env, jobject, jint srcColor, jint modeHandle) {
+    static jlong CreateBlendModeFilter(JNIEnv* env, jobject, jint srcColor, jint modeHandle) {
         SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
         return reinterpret_cast<jlong>(SkColorFilter::MakeModeFilter(srcColor, mode).release());
     }
@@ -61,8 +61,8 @@
     {"nativeGetFinalizer", "()J", (void*) SkColorFilterGlue::GetNativeFinalizer }
 };
 
-static const JNINativeMethod porterduff_methods[] = {
-    { "native_CreatePorterDuffFilter", "(II)J", (void*) SkColorFilterGlue::CreatePorterDuffFilter },
+static const JNINativeMethod blendmode_methods[] = {
+    { "native_CreateBlendModeFilter", "(II)J", (void*) SkColorFilterGlue::CreateBlendModeFilter },
 };
 
 static const JNINativeMethod lighting_methods[] = {
@@ -76,8 +76,10 @@
 int register_android_graphics_ColorFilter(JNIEnv* env) {
     android::RegisterMethodsOrDie(env, "android/graphics/ColorFilter", colorfilter_methods,
                                   NELEM(colorfilter_methods));
-    android::RegisterMethodsOrDie(env, "android/graphics/PorterDuffColorFilter", porterduff_methods,
-                                  NELEM(porterduff_methods));
+    android::RegisterMethodsOrDie(env, "android/graphics/PorterDuffColorFilter", blendmode_methods,
+                                  NELEM(blendmode_methods));
+    android::RegisterMethodsOrDie(env, "android/graphics/BlendModeColorFilter", blendmode_methods,
+                                  NELEM(blendmode_methods));
     android::RegisterMethodsOrDie(env, "android/graphics/LightingColorFilter", lighting_methods,
                                   NELEM(lighting_methods));
     android::RegisterMethodsOrDie(env, "android/graphics/ColorMatrixColorFilter",
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index c249e20..df24bc4 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -845,12 +845,23 @@
         static_assert(9 == static_cast<int>(SkBlendMode::kSrcATop), "xfermode_mismatch");
         static_assert(10 == static_cast<int>(SkBlendMode::kDstATop), "xfermode_mismatch");
         static_assert(11 == static_cast<int>(SkBlendMode::kXor), "xfermode_mismatch");
-        static_assert(16 == static_cast<int>(SkBlendMode::kDarken), "xfermode_mismatch");
-        static_assert(17 == static_cast<int>(SkBlendMode::kLighten), "xfermode_mismatch");
+        static_assert(12 == static_cast<int>(SkBlendMode::kPlus), "xfermode_mismatch");
         static_assert(13 == static_cast<int>(SkBlendMode::kModulate), "xfermode_mismatch");
         static_assert(14 == static_cast<int>(SkBlendMode::kScreen), "xfermode_mismatch");
-        static_assert(12 == static_cast<int>(SkBlendMode::kPlus), "xfermode_mismatch");
         static_assert(15 == static_cast<int>(SkBlendMode::kOverlay), "xfermode_mismatch");
+        static_assert(16 == static_cast<int>(SkBlendMode::kDarken), "xfermode_mismatch");
+        static_assert(17 == static_cast<int>(SkBlendMode::kLighten), "xfermode_mismatch");
+        static_assert(18 == static_cast<int>(SkBlendMode::kColorDodge), "xfermode mismatch");
+        static_assert(19 == static_cast<int>(SkBlendMode::kColorBurn), "xfermode mismatch");
+        static_assert(20 == static_cast<int>(SkBlendMode::kHardLight), "xfermode mismatch");
+        static_assert(21 == static_cast<int>(SkBlendMode::kSoftLight), "xfermode mismatch");
+        static_assert(22 == static_cast<int>(SkBlendMode::kDifference), "xfermode mismatch");
+        static_assert(23 == static_cast<int>(SkBlendMode::kExclusion), "xfermode mismatch");
+        static_assert(24 == static_cast<int>(SkBlendMode::kMultiply), "xfermode mismatch");
+        static_assert(25 == static_cast<int>(SkBlendMode::kHue), "xfermode mismatch");
+        static_assert(26 == static_cast<int>(SkBlendMode::kSaturation), "xfermode mismatch");
+        static_assert(27 == static_cast<int>(SkBlendMode::kColor), "xfermode mismatch");
+        static_assert(28 == static_cast<int>(SkBlendMode::kLuminosity), "xfermode mismatch");
 
         SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 4f8bbc1..3329e20 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -919,7 +919,7 @@
     return IPCThreadState::self()->clearCallingWorkSource();
 }
 
-static void android_os_Binder_restoreCallingWorkSource(long token)
+static void android_os_Binder_restoreCallingWorkSource(jlong token)
 {
     IPCThreadState::self()->restoreCallingWorkSource(token);
 }
diff --git a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
index b708735..24bafca 100644
--- a/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
+++ b/core/jni/com_android_internal_net_NetworkStatsFactory.cpp
@@ -35,7 +35,6 @@
 #include "bpf/BpfUtils.h"
 #include "netdbpf/BpfNetworkStats.h"
 
-using android::bpf::hasBpfSupport;
 using android::bpf::parseBpfNetworkStatsDetail;
 using android::bpf::stats_line;
 
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 4aa88e7..7032081 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -99,7 +99,8 @@
   MOUNT_EXTERNAL_DEFAULT = 1,
   MOUNT_EXTERNAL_READ = 2,
   MOUNT_EXTERNAL_WRITE = 3,
-  MOUNT_EXTERNAL_FULL = 4,
+  MOUNT_EXTERNAL_INSTALLER = 4,
+  MOUNT_EXTERNAL_FULL = 5,
 };
 
 // Must match values in com.android.internal.os.Zygote.
@@ -446,6 +447,22 @@
     return true;
 }
 
+static bool bindMount(const std::string& sourceDir, const std::string& targetDir,
+        std::string* error_msg) {
+    if (TEMP_FAILURE_RETRY(mount(sourceDir.c_str(), targetDir.c_str(),
+            nullptr, MS_BIND | MS_REC, nullptr)) == -1) {
+        *error_msg = CREATE_ERROR("Failed to mount %s to %s: %s",
+                sourceDir.c_str(), targetDir.c_str(), strerror(errno));
+        return false;
+    }
+    if (TEMP_FAILURE_RETRY(mount(nullptr, targetDir.c_str(),
+            nullptr, MS_SLAVE | MS_REC, nullptr)) == -1) {
+        *error_msg = CREATE_ERROR("Failed to set MS_SLAVE for %s", targetDir.c_str());
+        return false;
+    }
+    return true;
+}
+
 static bool mountPkgSpecificDir(const std::string& mntSourceRoot,
         const std::string& mntTargetRoot, const std::string& packageName,
         const char* dirName, std::string* error_msg) {
@@ -453,22 +470,12 @@
             mntSourceRoot.c_str(), dirName, packageName.c_str());
     std::string mntTargetDir = StringPrintf("%s/Android/%s/%s",
             mntTargetRoot.c_str(), dirName, packageName.c_str());
-    if (TEMP_FAILURE_RETRY(mount(mntSourceDir.c_str(), mntTargetDir.c_str(),
-            nullptr, MS_BIND | MS_REC, nullptr)) == -1) {
-        *error_msg = CREATE_ERROR("Failed to mount %s to %s: %s",
-                mntSourceDir.c_str(), mntTargetDir.c_str(), strerror(errno));
-        return false;
-    }
-    if (TEMP_FAILURE_RETRY(mount(nullptr, mntTargetDir.c_str(),
-            nullptr, MS_SLAVE | MS_REC, nullptr)) == -1) {
-        *error_msg = CREATE_ERROR("Failed to set MS_SLAVE for %s", mntTargetDir.c_str());
-        return false;
-    }
-    return true;
+    return bindMount(mntSourceDir, mntTargetDir, error_msg);
 }
 
 static bool preparePkgSpecificDirs(const std::vector<std::string>& packageNames,
-        const std::vector<std::string>& volumeLabels, userid_t userId, std::string* error_msg) {
+        const std::vector<std::string>& volumeLabels, bool mountAllObbs,
+        userid_t userId, std::string* error_msg) {
     for (auto& label : volumeLabels) {
         std::string mntSource = StringPrintf("/mnt/runtime/write/%s", label.c_str());
         std::string mntTarget = StringPrintf("/storage/%s", label.c_str());
@@ -479,7 +486,14 @@
         for (auto& package : packageNames) {
             mountPkgSpecificDir(mntSource, mntTarget, package, "data", error_msg);
             mountPkgSpecificDir(mntSource, mntTarget, package, "media", error_msg);
-            mountPkgSpecificDir(mntSource, mntTarget, package, "obb", error_msg);
+            if (!mountAllObbs) {
+                mountPkgSpecificDir(mntSource, mntTarget, package, "obb", error_msg);
+            }
+        }
+        if (mountAllObbs) {
+            StringAppendF(&mntSource, "/Android/obb");
+            StringAppendF(&mntTarget, "/Android/obb");
+            bindMount(mntSource, mntTarget, error_msg);
         }
     }
     return true;
@@ -500,7 +514,7 @@
         storageSource = "/mnt/runtime/read";
     } else if (mount_mode == MOUNT_EXTERNAL_WRITE) {
         storageSource = "/mnt/runtime/write";
-    } else if (mount_mode != MOUNT_EXTERNAL_FULL && !force_mount_namespace) {
+    } else if (mount_mode == MOUNT_EXTERNAL_NONE && !force_mount_namespace) {
         // Sane default of no storage visible
         return true;
     }
@@ -568,12 +582,28 @@
                         pkgSandboxDir.c_str(), strerror(errno));
                 return false;
             }
+            if (access("/storage/obb_mount", F_OK) == 0) {
+                if (mount_mode != MOUNT_EXTERNAL_INSTALLER) {
+                    remove("/storage/obb_mount");
+                }
+            } else {
+                if (mount_mode == MOUNT_EXTERNAL_INSTALLER) {
+                    int fd = TEMP_FAILURE_RETRY(open("/storage/obb_mount",
+                            O_RDWR | O_CREAT, 0660));
+                    if (fd == -1) {
+                        *error_msg = CREATE_ERROR("Couldn't create /storage/obb_mount: %s",
+                                strerror(errno));
+                        return false;
+                    }
+                    close(fd);
+                }
+            }
             // If the sandbox was already created by vold, only then set up the bind mounts for
             // pkg specific directories. Otherwise, leave as is and bind mounts will be taken
             // care of by vold later.
             if (sandboxAlreadyCreated) {
                 if (!preparePkgSpecificDirs(packages_for_uid, visible_vol_ids,
-                        user_id, error_msg)) {
+                        mount_mode == MOUNT_EXTERNAL_INSTALLER, user_id, error_msg)) {
                     return false;
                 }
             }
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index 514f306..3e1c5a3 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -82,5 +82,7 @@
 
     // OPEN: WifiDppEnrolleeActivity (android.settings.WIFI_DPP_ENROLLEE_XXX action intents)
     SETTINGS_WIFI_DPP_ENROLLEE = 1596;
-}
 
+    // OPEN: Settings > Apps & Notifications -> Special app access -> Financial Apps Sms Access
+    SETTINGS_FINANCIAL_APPS_SMS_ACCESS = 1597;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 77efbec..dca15bd 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1121,10 +1121,10 @@
                 android:protectionLevel="normal" />
 
     <!--Allows an app which implements the
-        {@link InCallService} API to be eligible to be enabled as a calling companion app. This
-        means that the Telecom framework will bind to the app's InCallService implementation when
-        there are calls active. The app can use the InCallService API to view information about
-        calls on the system and control these calls.
+        {@link android.telecom.InCallService InCallService} API to be eligible to be enabled as a
+        calling companion app. This means that the Telecom framework will bind to the app's
+        InCallService implementation when there are calls active. The app can use the InCallService
+        API to view information about calls on the system and control these calls.
         <p>Protection level: normal
     -->
     <permission android:name="android.permission.CALL_COMPANION_APP"
@@ -4696,6 +4696,10 @@
                  android:permission="android.permission.BIND_JOB_SERVICE">
         </service>
 
+        <service android:name="com.android.server.pm.DynamicCodeLoggingService"
+                 android:permission="android.permission.BIND_JOB_SERVICE">
+        </service>
+
         <service android:name="com.android.server.PruneInstantAppsJobService"
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
diff --git a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
index d1dbd3c..5664df6 100644
--- a/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
+++ b/core/tests/coretests/src/android/os/BinderWorkSourceTest.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.platform.test.annotations.Presubmit;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.LargeTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -37,6 +38,7 @@
  * Test whether Binder calls work source is propagated correctly.
  */
 @LargeTest
+@Presubmit
 @RunWith(AndroidJUnit4.class)
 public class BinderWorkSourceTest {
     private static Context sContext;
@@ -125,8 +127,10 @@
         Binder.setCallingWorkSourceUid(UID);
         long token = Binder.clearCallingWorkSource();
         Binder.restoreCallingWorkSource(token);
+        assertEquals(UID, Binder.getCallingWorkSourceUid());
 
         assertEquals(UID, mService.getIncomingWorkSourceUid());
+        // Still the same after the binder transaction.
         assertEquals(UID, Binder.getCallingWorkSourceUid());
     }
 
diff --git a/data/etc/OWNERS b/data/etc/OWNERS
index bbec474..ea66ee3 100644
--- a/data/etc/OWNERS
+++ b/data/etc/OWNERS
@@ -1 +1 @@
-per-file privapp-permissions-platform.xml = hackbod@android.com, jsharkey@android.com, svetoslavganov@google.com, toddke@google.com, yamasani@google.com
+per-file privapp-permissions-platform.xml = hackbod@android.com, jsharkey@android.com, svetoslavganov@google.com, toddke@google.com, yamasani@google.com, cbrubaker@google.com, jeffv@google.com, moltmann@google.com
diff --git a/docs/html/reference/images/graphics/blendmode_CLEAR.png b/docs/html/reference/images/graphics/blendmode_CLEAR.png
new file mode 100644
index 0000000..979782a
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_CLEAR.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_COLOR.png b/docs/html/reference/images/graphics/blendmode_COLOR.png
new file mode 100644
index 0000000..2f41bfb
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_COLOR.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_COLOR_BURN.png b/docs/html/reference/images/graphics/blendmode_COLOR_BURN.png
new file mode 100644
index 0000000..26059ce
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_COLOR_BURN.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_COLOR_DODGE.png b/docs/html/reference/images/graphics/blendmode_COLOR_DODGE.png
new file mode 100644
index 0000000..922f1d9
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_COLOR_DODGE.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_DARKEN.png b/docs/html/reference/images/graphics/blendmode_DARKEN.png
new file mode 100644
index 0000000..6c04aa3
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_DARKEN.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_DIFFERENCE.png b/docs/html/reference/images/graphics/blendmode_DIFFERENCE.png
new file mode 100644
index 0000000..aab2bcb
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_DIFFERENCE.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_DST.png b/docs/html/reference/images/graphics/blendmode_DST.png
new file mode 100644
index 0000000..16f96b4
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_DST.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_DST_ATOP.png b/docs/html/reference/images/graphics/blendmode_DST_ATOP.png
new file mode 100644
index 0000000..d0ae2cd
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_DST_ATOP.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_DST_IN.png b/docs/html/reference/images/graphics/blendmode_DST_IN.png
new file mode 100644
index 0000000..9befe27
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_DST_IN.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_DST_OUT.png b/docs/html/reference/images/graphics/blendmode_DST_OUT.png
new file mode 100644
index 0000000..e9227e9f
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_DST_OUT.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_DST_OVER.png b/docs/html/reference/images/graphics/blendmode_DST_OVER.png
new file mode 100644
index 0000000..015be0a
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_DST_OVER.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_EXCLUSION.png b/docs/html/reference/images/graphics/blendmode_EXCLUSION.png
new file mode 100644
index 0000000..307dec9
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_EXCLUSION.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_HARD_LIGHT.png b/docs/html/reference/images/graphics/blendmode_HARD_LIGHT.png
new file mode 100644
index 0000000..3b62b98
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_HARD_LIGHT.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_HUE.png b/docs/html/reference/images/graphics/blendmode_HUE.png
new file mode 100644
index 0000000..012bd33
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_HUE.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_LIGHTEN.png b/docs/html/reference/images/graphics/blendmode_LIGHTEN.png
new file mode 100644
index 0000000..1c3be65
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_LIGHTEN.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_LUMINOSITY.png b/docs/html/reference/images/graphics/blendmode_LUMINOSITY.png
new file mode 100644
index 0000000..3549082
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_LUMINOSITY.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_MODULATE.png b/docs/html/reference/images/graphics/blendmode_MODULATE.png
new file mode 100644
index 0000000..ed1b59d
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_MODULATE.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_MULTIPLY.png b/docs/html/reference/images/graphics/blendmode_MULTIPLY.png
new file mode 100644
index 0000000..c8c7ccb
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_MULTIPLY.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_OVERLAY.png b/docs/html/reference/images/graphics/blendmode_OVERLAY.png
new file mode 100644
index 0000000..6962f92
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_OVERLAY.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_PLUS.png b/docs/html/reference/images/graphics/blendmode_PLUS.png
new file mode 100644
index 0000000..015fa2b
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_PLUS.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_SATURATION.png b/docs/html/reference/images/graphics/blendmode_SATURATION.png
new file mode 100644
index 0000000..5ac96c3
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_SATURATION.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_SCREEN.png b/docs/html/reference/images/graphics/blendmode_SCREEN.png
new file mode 100644
index 0000000..d2d70d2
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_SCREEN.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_SOFT_LIGHT.png b/docs/html/reference/images/graphics/blendmode_SOFT_LIGHT.png
new file mode 100644
index 0000000..89fbacd
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_SOFT_LIGHT.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_SRC.png b/docs/html/reference/images/graphics/blendmode_SRC.png
new file mode 100644
index 0000000..990ec94
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_SRC.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_SRC_ATOP.png b/docs/html/reference/images/graphics/blendmode_SRC_ATOP.png
new file mode 100644
index 0000000..d574dfd
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_SRC_ATOP.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_SRC_IN.png b/docs/html/reference/images/graphics/blendmode_SRC_IN.png
new file mode 100644
index 0000000..dda45d7
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_SRC_IN.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_SRC_OUT.png b/docs/html/reference/images/graphics/blendmode_SRC_OUT.png
new file mode 100644
index 0000000..f5d43c1
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_SRC_OUT.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_SRC_OVER.png b/docs/html/reference/images/graphics/blendmode_SRC_OVER.png
new file mode 100644
index 0000000..b1a405b
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_SRC_OVER.png
Binary files differ
diff --git a/docs/html/reference/images/graphics/blendmode_XOR.png b/docs/html/reference/images/graphics/blendmode_XOR.png
new file mode 100644
index 0000000..31c110d
--- /dev/null
+++ b/docs/html/reference/images/graphics/blendmode_XOR.png
Binary files differ
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index 3db240b..ca9dc47 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -241,10 +241,22 @@
         nDrawColor(mNativeCanvasWrapper, color, PorterDuff.Mode.SRC_OVER.nativeInt);
     }
 
+    /**
+     * @deprecated use {@link Canvas#drawColor(int, BlendMode)}
+     */
+    @Deprecated
     public void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
         nDrawColor(mNativeCanvasWrapper, color, mode.nativeInt);
     }
 
+    /**
+     * Make lint happy.
+     * See {@link Canvas#drawColor(int, BlendMode)}
+     */
+    public void drawColor(@ColorInt int color, @NonNull BlendMode mode) {
+        nDrawColor(mNativeCanvasWrapper, color, mode.getXfermode().porterDuffMode);
+    }
+
     public void drawLine(float startX, float startY, float stopX, float stopY,
             @NonNull Paint paint) {
         throwIfHasHwBitmapInSwMode(paint);
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index 4de7ca7..901c211 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -201,12 +201,21 @@
         nDrawColor(mNativeCanvasWrapper, color, PorterDuff.Mode.SRC_OVER.nativeInt);
     }
 
+    /**
+     * @deprecated use {@link #drawColor(int, BlendMode)} instead
+     */
+    @Deprecated
     @Override
     public final void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
         nDrawColor(mNativeCanvasWrapper, color, mode.nativeInt);
     }
 
     @Override
+    public final void drawColor(@ColorInt int color, @NonNull BlendMode mode) {
+        nDrawColor(mNativeCanvasWrapper, color, mode.getXfermode().porterDuffMode);
+    }
+
+    @Override
     public final void drawLine(float startX, float startY, float stopX, float stopY,
             @NonNull Paint paint) {
         nDrawLine(mNativeCanvasWrapper, startX, startY, stopX, stopY, paint.getNativeInstance());
diff --git a/graphics/java/android/graphics/BlendMode.java b/graphics/java/android/graphics/BlendMode.java
new file mode 100644
index 0000000..39392c8
--- /dev/null
+++ b/graphics/java/android/graphics/BlendMode.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+public enum BlendMode {
+
+    /**
+     * <p>
+     *  <img src="{@docRoot}reference/android/images/graphics/blendmode_CLEAR.png" />
+     *  <figcaption>Destination pixels covered by the source are cleared to 0.</figcaption>
+     * </p>
+     * <p>\(\alpha_{out} = 0\)</p>
+     * <p>\(C_{out} = 0\)</p>
+     */
+    CLEAR(0),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_SRC.png" />
+     *     <figcaption>The source pixels replace the destination pixels.</figcaption>
+     * </p>
+     * <p>\(\alpha_{out} = \alpha_{src}\)</p>
+     * <p>\(C_{out} = C_{src}\)</p>
+     */
+    SRC(1),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_DST.png" />
+     *     <figcaption>The source pixels are discarded, leaving the destination intact.</figcaption>
+     * </p>
+     * <p>\(\alpha_{out} = \alpha_{dst}\)</p>
+     * <p>\(C_{out} = C_{dst}\)</p>
+     */
+    DST(2),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_SRC_OVER.png" />
+     *     <figcaption>The source pixels are drawn over the destination pixels.</figcaption>
+     * </p>
+     * <p>\(\alpha_{out} = \alpha_{src} + (1 - \alpha_{src}) * \alpha_{dst}\)</p>
+     * <p>\(C_{out} = C_{src} + (1 - \alpha_{src}) * C_{dst}\)</p>
+     */
+    SRC_OVER(3),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_DST_OVER.png" />
+     *     <figcaption>The source pixels are drawn behind the destination pixels.</figcaption>
+     * </p>
+     * <p>\(\alpha_{out} = \alpha_{dst} + (1 - \alpha_{dst}) * \alpha_{src}\)</p>
+     * <p>\(C_{out} = C_{dst} + (1 - \alpha_{dst}) * C_{src}\)</p>
+     */
+    DST_OVER(4),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_SRC_IN.png" />
+     *     <figcaption>Keeps the source pixels that cover the destination pixels,
+     *     discards the remaining source and destination pixels.</figcaption>
+     * </p>
+     * <p>\(\alpha_{out} = \alpha_{src} * \alpha_{dst}\)</p>
+     * <p>\(C_{out} = C_{src} * \alpha_{dst}\)</p>
+     */
+    SRC_IN(5),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_DST_IN.png" />
+     *     <figcaption>Keeps the destination pixels that cover source pixels,
+     *     discards the remaining source and destination pixels.</figcaption>
+     * </p>
+     * <p>\(\alpha_{out} = \alpha_{src} * \alpha_{dst}\)</p>
+     * <p>\(C_{out} = C_{dst} * \alpha_{src}\)</p>
+     */
+    DST_IN(6),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_SRC_OUT.png" />
+     *     <figcaption>Keeps the source pixels that do not cover destination pixels.
+     *     Discards source pixels that cover destination pixels. Discards all
+     *     destination pixels.</figcaption>
+     * </p>
+     * <p>\(\alpha_{out} = (1 - \alpha_{dst}) * \alpha_{src}\)</p>
+     * <p>\(C_{out} = (1 - \alpha_{dst}) * C_{src}\)</p>
+     */
+    SRC_OUT(7),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_DST_OUT.png" />
+     *     <figcaption>Keeps the destination pixels that are not covered by source pixels.
+     *     Discards destination pixels that are covered by source pixels. Discards all
+     *     source pixels.</figcaption>
+     * </p>
+     * <p>\(\alpha_{out} = (1 - \alpha_{src}) * \alpha_{dst}\)</p>
+     * <p>\(C_{out} = (1 - \alpha_{src}) * C_{dst}\)</p>
+     */
+    DST_OUT(8),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_SRC_ATOP.png" />
+     *     <figcaption>Discards the source pixels that do not cover destination pixels.
+     *     Draws remaining source pixels over destination pixels.</figcaption>
+     * </p>
+     * <p>\(\alpha_{out} = \alpha_{dst}\)</p>
+     * <p>\(C_{out} = \alpha_{dst} * C_{src} + (1 - \alpha_{src}) * C_{dst}\)</p>
+     */
+    SRC_ATOP(9),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_DST_ATOP.png" />
+     *     <figcaption>Discards the destination pixels that are not covered by source pixels.
+     *     Draws remaining destination pixels over source pixels.</figcaption>
+     * </p>
+     * <p>\(\alpha_{out} = \alpha_{src}\)</p>
+     * <p>\(C_{out} = \alpha_{src} * C_{dst} + (1 - \alpha_{dst}) * C_{src}\)</p>
+     */
+    DST_ATOP(10),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_XOR.png" />
+     *     <figcaption>Discards the source and destination pixels where source pixels
+     *     cover destination pixels. Draws remaining source pixels.</figcaption>
+     * </p>
+     * <p>
+     *     \(\alpha_{out} = (1 - \alpha_{dst}) * \alpha_{src} + (1 - \alpha_{src}) * \alpha_{dst}\)
+     * </p>
+     * <p>\(C_{out} = (1 - \alpha_{dst}) * C_{src} + (1 - \alpha_{src}) * C_{dst}\)</p>
+     */
+    XOR(11),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_PLUS.png" />
+     *     <figcaption>Adds the source pixels to the destination pixels and saturates
+     *     the result.</figcaption>
+     * </p>
+     * <p>\(\alpha_{out} = max(0, min(\alpha_{src} + \alpha_{dst}, 1))\)</p>
+     * <p>\(C_{out} = max(0, min(C_{src} + C_{dst}, 1))\)</p>
+     */
+    PLUS(12),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_MODULATE.png" />
+     *     <figcaption>Multiplies the source and destination pixels.</figcaption>
+     * </p>
+     * <p>\(\alpha_{out} = \alpha_{src} * \alpha_{dst}\)</p>
+     * <p>\(C_{out} = C_{src} * C_{dst}\)</p>
+     *
+     */
+    MODULATE(13),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_SCREEN.png" />
+     *     <figcaption>
+     *         Adds the source and destination pixels, then subtracts the
+     *         source pixels multiplied by the destination.
+     *     </figcaption>
+     * </p>
+     * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
+     * <p>\(C_{out} = C_{src} + C_{dst} - C_{src} * C_{dst}\)</p>
+     */
+    SCREEN(14),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_OVERLAY.png" />
+     *     <figcaption>
+     *         Multiplies or screens the source and destination depending on the
+     *         destination color.
+     *     </figcaption>
+     * </p>
+     * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
+     * <p>\(\begin{equation}
+     * C_{out} = \begin{cases} 2 * C_{src} * C_{dst} & 2 * C_{dst} \lt \alpha_{dst} \\
+     * \alpha_{src} * \alpha_{dst} - 2 (\alpha_{dst} - C_{src}) (\alpha_{src} - C_{dst}) &
+     * otherwise \end{cases}
+     * \end{equation}\)</p>
+     */
+    OVERLAY(15),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_DARKEN.png" />
+     *     <figcaption>
+     *         Retains the smallest component of the source and
+     *         destination pixels.
+     *     </figcaption>
+     * </p>
+     * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
+     * <p>
+     *     \(C_{out} =
+     *     (1 - \alpha_{dst}) * C_{src} + (1 - \alpha_{src}) * C_{dst} + min(C_{src}, C_{dst})\)
+     * </p>
+     */
+    DARKEN(16),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_LIGHTEN.png" />
+     *     <figcaption>Retains the largest component of the source and
+     *     destination pixel.</figcaption>
+     * </p>
+     * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
+     * <p>
+     *     \(C_{out} =
+     *      (1 - \alpha_{dst}) * C_{src} + (1 - \alpha_{src}) * C_{dst} + max(C_{src}, C_{dst})\)
+     * </p>
+     */
+    LIGHTEN(17),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_COLOR_DODGE.png" />
+     *     <figcaption>Makes destination brighter to reflect source.</figcaption>
+     * </p>
+     * <p>
+     *     \(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)
+     * </p>
+     * <p>
+     *      \begin{equation}
+     *      C_{out} =
+     *      \begin{cases}
+     *          C_{src} * (1 - \alpha_{dst}) & C_{dst} = 0 \\
+     *          C_{src} + \alpha_{dst}*(1 - \alpha_{src}) & C_{src} = \alpha_{src} \\
+     *          \alpha_{src} * min(\alpha_{dst}, C_{dst} * \alpha_{src}/(\alpha_{src} - C_{src}))
+     *              + C_{src} *(1 - \alpha_{dst} + \alpha_{dst}*(1 - \alpha_{src}) & otherwise
+     *      \end{cases}
+     *      \end{equation}
+     * </p>
+     */
+    COLOR_DODGE(18),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_COLOR_BURN.png" />
+     *     <figcaption>Makes destination darker to reflect source.</figcaption>
+     * </p>
+     * <p>
+     *     \(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)
+     * </p>
+     * <p>
+     *     \begin{equation}
+     *     C_{out} =
+     *     \begin{cases}
+     *         C_{dst} + C_{src}*(1 - \alpha_{dst}) & C_{dst} = \alpha_{dst} \\
+     *         \alpha_{dst}*(1 - \alpha_{src}) & C_{src} = 0 \\
+     *         \alpha_{src}*(\alpha_{dst} - min(\alpha_{dst}, (\alpha_{dst}
+     *         - C_{dst})*\alpha_{src}/C_{src}))
+     *         + C_{src} * (1 - \alpha_{dst}) + \alpha_{dst}*(1-\alpha_{src}) & otherwise
+     *     \end{cases}
+     *     \end{equation}
+     * </p>
+     */
+    COLOR_BURN(19),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_HARD_LIGHT.png" />
+     *     <figcaption>Makes destination lighter or darker, depending on source.</figcaption>
+     * </p>
+     * <p>
+     *     \(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)
+     * </p>
+     * <p>
+     *     \begin{equation}
+     *      C_{out} =
+     *      \begin{cases}
+     *           2*C_{src}*C_{dst} & C_{src}*(1-\alpha_{dst}) + C_{dst}*(1-\alpha_{src}) + 2*C_{src}
+     *              \leq \alpha_{src} \\
+     *           \alpha_{src}*\alpha_{dst}- 2*(\alpha_{dst} - C_{dst})*(\alpha_{src} - C_{src})
+     *              & otherwise
+     *      \end{cases}
+     *      \end{equation}
+     * </p>
+     */
+    HARD_LIGHT(20),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_SOFT_LIGHT.png" />
+     *     <figcaption>Makes destination lighter or darker, depending on source.</figcaption>
+     * </p>
+     * <p>
+     *     Where
+     *       \begin{equation}
+     *       m =
+     *          \begin{cases}
+     *              C_{dst} / \alpha_{dst} & \alpha_{dst} \gt 0 \\
+     *              0 & otherwise
+     *          \end{cases}
+     *       \end{equation}
+     * </p>
+     * <p>
+     *       \begin{equation}
+     *       g =
+     *          \begin{cases}
+     *              (16 * m * m + 4 * m) * (m - 1) + 7 * m & 4 * C_{dst} \leq \alpha_{dst} \\
+     *              \sqrt m - m & otherwise
+     *          \end{cases}
+     *       \end{equation}
+     * </p>
+     * <p>
+     *       \begin{equation}
+     *       f =
+     *          \begin{cases}
+     *              C_{dst} * (\alpha_{src} + (2 * C_{src} - \alpha_{src}) * (1 - m))
+     *                  & 2 * C_{src} \leq \alpha_{src} \\
+     *              C_{dst} * \alpha_{src} + \alpha_{dst} * (2 * C_{src} - \alpha_{src}) * g
+     *                  & otherwise
+     *          \end{cases}
+     *       \end{equation}
+     * </p>
+     * <p>
+     *       \begin{equation}
+     *          \alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}
+     *       \end{equation}
+     *       \begin{equation}
+     *          C_{out} = C_{src} / \alpha_{dst} + C_{dst} / \alpha_{src} + f
+     *       \end{equation}
+     * </p>
+     */
+    SOFT_LIGHT(21),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_DIFFERENCE.png" />
+     *     <figcaption>Subtracts darker from lighter with higher contrast.</figcaption>
+     * </p>
+     * <p>
+     *     \begin{equation}
+     *          \alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}
+     *     \end{equation}
+     * </p>
+     * <p>
+     *     \begin{equation}
+     *           C_{out} = C_{src} + C_{dst} - 2 * min(C_{src}
+     *                       * \alpha_{dst}, C_{dst} * \alpha_{src})
+     *     \end{equation}
+     * </p>
+     */
+    DIFFERENCE(22),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_DIFFERENCE.png" />
+     *     <figcaption>Subtracts darker from lighter with lower contrast.</figcaption>
+     * </p>
+     * <p>
+     *     \begin{equation}
+     *          \alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}
+     *     \end{equation}
+     * </p>
+     * <p>
+     *     \begin{equation}
+     *          C_{out} = C_{src} + C_{dst} - 2 * C_{src} * C_{dst}
+     *     \end{equation}
+     * </p>
+     */
+    EXCLUSION(23),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_MODULATE.png" />
+     *     <figcaption>Multiplies the source and destination pixels.</figcaption>
+     * </p>
+     * <p>\(\alpha_{out} = \alpha_{src} + \alpha_{dst} - \alpha_{src} * \alpha_{dst}\)</p>
+     * <p>\(C_{out} =
+     *      C_{src} * (1 - \alpha_{dst}) + C_{dst} * (1 - \alpha_{src}) + (C_{src} * C_{dst})\)
+     * </p>
+     */
+    MULTIPLY(24),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_HUE.png" />
+     *     <figcaption>
+     *         Replaces hue of destination with hue of source, leaving saturation
+     *         and luminosity unchanged.
+     *     </figcaption>
+     * </p>
+     */
+    HUE(25),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_SATURATION.png" />
+     *     <figcaption>
+     *          Replaces saturation of destination saturation hue of source, leaving hue and
+     *          luminosity unchanged.
+     *     </figcaption>
+     * </p>
+     */
+    SATURATION(26),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_COLOR.png" />
+     *     <figcaption>
+     *          Replaces hue and saturation of destination with hue and saturation of source,
+     *          leaving luminosity unchanged.
+     *     </figcaption>
+     * </p>
+     */
+    COLOR(27),
+
+    /**
+     * <p>
+     *     <img src="{@docRoot}reference/android/images/graphics/blendmode_LUMINOSITY.png" />
+     *     <figcaption>
+     *          Replaces luminosity of destination with luminosity of source, leaving hue and
+     *          saturation unchanged.
+     *     </figcaption>
+     * </p>
+     */
+    LUMINOSITY(28);
+
+    private static final BlendMode[] BLEND_MODES = values();
+
+    /**
+     * @hide
+     */
+    public static @Nullable BlendMode fromValue(int value) {
+        for (BlendMode mode : BLEND_MODES) {
+            if (mode.mXfermode.porterDuffMode == value) {
+                return mode;
+            }
+        }
+        return null;
+    }
+
+    @NonNull
+    private final Xfermode mXfermode;
+
+    BlendMode(int mode) {
+        mXfermode = new Xfermode();
+        mXfermode.porterDuffMode = mode;
+    }
+
+    /**
+     * @hide
+     */
+    @NonNull
+    public Xfermode getXfermode() {
+        return mXfermode;
+    }
+}
diff --git a/graphics/java/android/graphics/BlendModeColorFilter.java b/graphics/java/android/graphics/BlendModeColorFilter.java
new file mode 100644
index 0000000..7caeb42
--- /dev/null
+++ b/graphics/java/android/graphics/BlendModeColorFilter.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.annotation.ColorInt;
+import android.annotation.NonNull;
+
+/**
+ * A color filter that can be used to tint the source pixels using a single
+ * color and a specific {@link BlendMode}.
+ */
+public final class BlendModeColorFilter extends ColorFilter {
+
+    @ColorInt final int mColor;
+    private final BlendMode mMode;
+
+    public BlendModeColorFilter(@ColorInt int color, @NonNull BlendMode mode) {
+        mColor = color;
+        mMode = mode;
+    }
+
+
+    /**
+     * Returns the ARGB color used to tint the source pixels when this filter
+     * is applied.
+     *
+     * @see Color
+     *
+     */
+    @ColorInt
+    public int getColor() {
+        return mColor;
+    }
+
+    /**
+     * Returns the Porter-Duff mode used to composite this color filter's
+     * color with the source pixel when this filter is applied.
+     *
+     * @see BlendMode
+     *
+     */
+    public BlendMode getMode() {
+        return mMode;
+    }
+
+    @Override
+    long createNativeInstance() {
+        return native_CreateBlendModeFilter(mColor, mMode.getXfermode().porterDuffMode);
+    }
+
+    @Override
+    public boolean equals(Object object) {
+        if (this == object) {
+            return true;
+        }
+        if (object == null || getClass() != object.getClass()) {
+            return false;
+        }
+        final BlendModeColorFilter other = (BlendModeColorFilter) object;
+        return other.mMode == mMode;
+    }
+
+    @Override
+    public int hashCode() {
+        return 31 *  mMode.hashCode() + mColor;
+    }
+
+    private static native long native_CreateBlendModeFilter(int srcColor, int blendmode);
+
+}
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 135c137..6798ab2 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1684,12 +1684,26 @@
      *
      * @param color the color to draw with
      * @param mode the porter-duff mode to apply to the color
+     *
+     * @deprecated use {@link #drawColor(int, BlendMode)} instead
      */
+    @Deprecated
     public void drawColor(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
         super.drawColor(color, mode);
     }
 
     /**
+     * Fill the entire canvas' bitmap (restricted to the current clip) with the specified color and
+     * blendmode.
+     *
+     * @param color the color to draw with
+     * @param mode the blendmode to apply to the color
+     */
+    public void drawColor(@ColorInt int color, @NonNull BlendMode mode) {
+        super.drawColor(color, mode);
+    }
+
+    /**
      * Draw a line segment with the specified start and stop x,y coordinates, using the specified
      * paint.
      * <p>
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 69ff3bc..6821282 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1168,12 +1168,29 @@
      * Get the paint's transfer mode object.
      *
      * @return the paint's transfer mode (or null)
+     *
+     * @deprecated use {@link #getBlendMode()} instead
      */
+    @Deprecated
     public Xfermode getXfermode() {
         return mXfermode;
     }
 
     /**
+     * Get the paint's blend mode object.
+     *
+     * @return the paint's blend mode (or null)
+     */
+    @Nullable
+    public BlendMode getBlendMode() {
+        if (mXfermode == null) {
+            return null;
+        } else {
+            return BlendMode.fromValue(mXfermode.porterDuffMode);
+        }
+    }
+
+    /**
      * Set or clear the transfer mode object. A transfer mode defines how
      * source pixels (generate by a drawing command) are composited with
      * the destination pixels (content of the render target).
@@ -1185,8 +1202,17 @@
      *
      * @param xfermode May be null. The xfermode to be installed in the paint
      * @return         xfermode
+     *
+     * @deprecated Use {@link #setBlendMode} to apply a Xfermode directly
+     * through usage of {@link BlendMode}
      */
+    @Deprecated
     public Xfermode setXfermode(Xfermode xfermode) {
+        return installXfermode(xfermode);
+    }
+
+    @Nullable
+    private Xfermode installXfermode(Xfermode xfermode) {
         int newMode = xfermode != null ? xfermode.porterDuffMode : Xfermode.DEFAULT;
         int curMode = mXfermode != null ? mXfermode.porterDuffMode : Xfermode.DEFAULT;
         if (newMode != curMode) {
@@ -1197,6 +1223,23 @@
     }
 
     /**
+     * Set or clear the blend mode. A blend mode defines how source pixels
+     * (generated by a drawing command) are composited with the destination pixels
+     * (content of the render target).
+     * <p />
+     * Pass null to clear any previous blend mode.
+     * As a convenience, the parameter passed is also returned.
+     * <p />
+     *
+     * @see BlendMode
+     *
+     * @param blendmode May be null. The blend mode to be installed in the paint
+     */
+    public void setBlendMode(@Nullable BlendMode blendmode) {
+        installXfermode(blendmode != null ? blendmode.getXfermode() : null);
+    }
+
+    /**
      * Get the paint's patheffect object.
      *
      * @return the paint's patheffect (or null)
diff --git a/graphics/java/android/graphics/PorterDuffColorFilter.java b/graphics/java/android/graphics/PorterDuffColorFilter.java
index 6665220..c2a8eb7 100644
--- a/graphics/java/android/graphics/PorterDuffColorFilter.java
+++ b/graphics/java/android/graphics/PorterDuffColorFilter.java
@@ -23,7 +23,11 @@
 /**
  * A color filter that can be used to tint the source pixels using a single
  * color and a specific {@link PorterDuff Porter-Duff composite mode}.
+ *
+ * @deprecated Consider using {@link BlendModeColorFilter} instead as it supports a wider
+ * set of blend modes than those defined in {@link PorterDuff.Mode}
  */
+@Deprecated
 public class PorterDuffColorFilter extends ColorFilter {
     @ColorInt
     private int mColor;
@@ -71,7 +75,7 @@
 
     @Override
     long createNativeInstance() {
-        return native_CreatePorterDuffFilter(mColor, mMode.nativeInt);
+        return native_CreateBlendModeFilter(mColor, mMode.nativeInt);
     }
 
     @Override
@@ -91,5 +95,5 @@
         return 31 *  mMode.hashCode() + mColor;
     }
 
-    private static native long native_CreatePorterDuffFilter(int srcColor, int porterDuffMode);
+    private static native long native_CreateBlendModeFilter(int srcColor, int blendmode);
 }
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 5bd59d4..09d0a58 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -595,7 +595,12 @@
      * <p class="note"><strong>Note:</strong> Setting a color filter disables
      * {@link #setTintList(ColorStateList) tint}.
      * </p>
+     *
+     * @see {@link #setColorFilter(ColorFilter)} }
+     * @deprecated use {@link #setColorFilter(ColorFilter)} with an instance
+     * of {@link android.graphics.BlendModeColorFilter}
      */
+    @Deprecated
     public void setColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
         if (getColorFilter() instanceof PorterDuffColorFilter) {
             PorterDuffColorFilter existing = (PorterDuffColorFilter) getColorFilter();
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 7480fa0..52e9ae1 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -25,10 +25,14 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.util.Log;
+
 import java.lang.ref.WeakReference;
-import java.nio.ByteOrder;
 import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Objects;
 import java.util.UUID;
 
 /**
@@ -225,35 +229,12 @@
      * The method {@link #queryEffects()} returns an array of Descriptors to facilitate effects
      * enumeration.
      */
-    public static class Descriptor {
+    public static final class Descriptor implements Parcelable {
 
         public Descriptor() {
         }
 
         /**
-         * @param type          UUID identifying the effect type. May be one of:
-         * {@link AudioEffect#EFFECT_TYPE_AEC}, {@link AudioEffect#EFFECT_TYPE_AGC},
-         * {@link AudioEffect#EFFECT_TYPE_BASS_BOOST}, {@link AudioEffect#EFFECT_TYPE_ENV_REVERB},
-         * {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, {@link AudioEffect#EFFECT_TYPE_NS},
-         * {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB},
-         * {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER},
-         * {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING}.
-         * @param uuid         UUID for this particular implementation
-         * @param connectMode  {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}
-         * @param name         human readable effect name
-         * @param implementor  human readable effect implementor name
-        *
-        */
-        public Descriptor(String type, String uuid, String connectMode,
-                String name, String implementor) {
-            this.type = UUID.fromString(type);
-            this.uuid = UUID.fromString(uuid);
-            this.connectMode = connectMode;
-            this.name = name;
-            this.implementor = implementor;
-        }
-
-        /**
          *  Indicates the generic type of the effect (Equalizer, Bass boost ...).
          *  One of {@link AudioEffect#EFFECT_TYPE_AEC},
          *  {@link AudioEffect#EFFECT_TYPE_AGC}, {@link AudioEffect#EFFECT_TYPE_BASS_BOOST},
@@ -289,7 +270,86 @@
          * Human readable effect implementor name
          */
         public String implementor;
-    };
+
+        /**
+         * @param type          UUID identifying the effect type. May be one of:
+         * {@link AudioEffect#EFFECT_TYPE_AEC}, {@link AudioEffect#EFFECT_TYPE_AGC},
+         * {@link AudioEffect#EFFECT_TYPE_BASS_BOOST}, {@link AudioEffect#EFFECT_TYPE_ENV_REVERB},
+         * {@link AudioEffect#EFFECT_TYPE_EQUALIZER}, {@link AudioEffect#EFFECT_TYPE_NS},
+         * {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB},
+         * {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER},
+         * {@link AudioEffect#EFFECT_TYPE_DYNAMICS_PROCESSING}.
+         * @param uuid         UUID for this particular implementation
+         * @param connectMode  {@link #EFFECT_INSERT} or {@link #EFFECT_AUXILIARY}
+         * @param name         human readable effect name
+         * @param implementor  human readable effect implementor name
+        *
+        */
+        public Descriptor(String type, String uuid, String connectMode,
+                String name, String implementor) {
+            this.type = UUID.fromString(type);
+            this.uuid = UUID.fromString(uuid);
+            this.connectMode = connectMode;
+            this.name = name;
+            this.implementor = implementor;
+        }
+
+        private Descriptor(Parcel in) {
+            type = UUID.fromString(in.readString());
+            uuid = UUID.fromString(in.readString());
+            connectMode = in.readString();
+            name = in.readString();
+            implementor = in.readString();
+        }
+
+        public static final Parcelable.Creator<Descriptor> CREATOR =
+                new Parcelable.Creator<Descriptor>() {
+                    /**
+                     * Rebuilds a Descriptor previously stored with writeToParcel().
+                     * @param p Parcel object to read the Descriptor from
+                     * @return a new Descriptor created from the data in the parcel
+                     */
+                    public Descriptor createFromParcel(Parcel p) {
+                        return new Descriptor(p);
+                    }
+                    public Descriptor[] newArray(int size) {
+                        return new Descriptor[size];
+                    }
+        };
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(type, uuid, connectMode, name, implementor);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeString(type.toString());
+            dest.writeString(uuid.toString());
+            dest.writeString(connectMode);
+            dest.writeString(name);
+            dest.writeString(implementor);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || !(o instanceof Descriptor)) return false;
+
+            Descriptor that = (Descriptor) o;
+
+            return (type.equals(that.type)
+                    && uuid.equals(that.uuid)
+                    && connectMode.equals(that.connectMode)
+                    && name.equals(that.name)
+                    && implementor.equals(that.implementor));
+        }
+    }
 
     /**
      * Effect connection mode is insert. Specifying an audio session ID when creating the effect
diff --git a/packages/CarSystemUI/Android.bp b/packages/CarSystemUI/Android.bp
index f244f9f..74d6605 100644
--- a/packages/CarSystemUI/Android.bp
+++ b/packages/CarSystemUI/Android.bp
@@ -26,6 +26,7 @@
     ],
 
     static_libs: [
+        "CarNotificationLib",
         "SystemUI-core",
         "SystemUIPluginLib",
         "SystemUISharedLib",
diff --git a/packages/CarSystemUI/res/values/config.xml b/packages/CarSystemUI/res/values/config.xml
index 452d61d..572737f 100644
--- a/packages/CarSystemUI/res/values/config.xml
+++ b/packages/CarSystemUI/res/values/config.xml
@@ -28,4 +28,31 @@
     <bool name="config_enableRightNavigationBar">false</bool>
     <bool name="config_enableBottomNavigationBar">true</bool>
 
+    <!-- SystemUI Services: The classes of the stuff to start. This is duplicated from core
+         SystemUi b/c it can't be overlayed at this level for now
+    -->
+    <string-array name="config_systemUIServiceComponents" translatable="false">
+        <item>com.android.systemui.Dependency</item>
+        <item>com.android.systemui.util.NotificationChannels</item>
+        <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
+        <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
+        <item>com.android.systemui.recents.Recents</item>
+        <item>com.android.systemui.volume.VolumeUI</item>
+        <item>com.android.systemui.stackdivider.Divider</item>
+        <item>com.android.systemui.SystemBars</item>
+        <item>com.android.systemui.usb.StorageNotification</item>
+        <item>com.android.systemui.power.PowerUI</item>
+        <item>com.android.systemui.media.RingtonePlayer</item>
+        <item>com.android.systemui.keyboard.KeyboardUI</item>
+        <item>com.android.systemui.pip.PipUI</item>
+        <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
+        <item>@string/config_systemUIVendorServiceComponent</item>
+        <item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
+        <item>com.android.systemui.LatencyTester</item>
+        <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
+        <item>com.android.systemui.ScreenDecorations</item>
+        <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
+        <item>com.android.systemui.SliceBroadcastRelayHandler</item>
+        <item>com.android.systemui.notifications.NotificationsUI</item>
+    </string-array>
 </resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java b/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java
new file mode 100644
index 0000000..cb92c42
--- /dev/null
+++ b/packages/CarSystemUI/src/com/android/systemui/notifications/NotificationsUI.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2018 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.notifications;
+
+import android.car.Car;
+import android.car.CarNotConnectedException;
+import android.car.drivingstate.CarUxRestrictionsManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.ServiceConnection;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import com.android.car.notification.CarNotificationListener;
+import com.android.car.notification.CarUxRestrictionManagerWrapper;
+import com.android.car.notification.NotificationViewController;
+import com.android.car.notification.PreprocessingManager;
+import com.android.systemui.R;
+import com.android.systemui.SystemUI;
+
+/**
+ * Standalone SystemUI for displaying Notifications that have been designed to be used in the car
+ */
+public class NotificationsUI extends SystemUI {
+
+    private static final String TAG = "NotificationsUI";
+    private CarNotificationListener mCarNotificationListener;
+    private CarUxRestrictionsManager mCarUxRestrictionsManager;
+    private Car mCar;
+    private ViewGroup mCarNotificationWindow;
+    private NotificationViewController mNotificationViewController;
+    private boolean mIsShowing;
+    private CarUxRestrictionManagerWrapper mCarUxRestrictionManagerWrapper =
+            new CarUxRestrictionManagerWrapper();
+
+    /**
+     * Inits the window that hosts the notifications and establishes the connections
+     * to the car related services.
+     */
+    @Override
+    public void start() {
+        WindowManager windowManager =
+                (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+        mCarNotificationListener = new CarNotificationListener();
+        mCarNotificationListener.registerAsSystemService(mContext, mCarUxRestrictionManagerWrapper);
+        mCar = Car.createCar(mContext, mCarConnectionListener);
+        mCar.connect();
+
+
+        mCarNotificationWindow = (ViewGroup) View.inflate(mContext,
+                R.layout.navigation_bar_window, null);
+        View.inflate(mContext,
+                com.android.car.notification.R.layout.notification_center_activity,
+                mCarNotificationWindow);
+        mCarNotificationWindow.findViewById(
+                com.android.car.notification.R.id.exit_button_container)
+                .setOnClickListener(v -> toggleShowingCarNotifications());
+
+        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY,
+                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+                PixelFormat.TRANSLUCENT);
+        layoutParams.setTitle("Car Notification Window");
+        // start in the hidden state
+        mCarNotificationWindow.setVisibility(View.GONE);
+        windowManager.addView(mCarNotificationWindow, layoutParams);
+        mNotificationViewController = new NotificationViewController(
+                mCarNotificationWindow
+                        .findViewById(com.android.car.notification.R.id.notification_view),
+                PreprocessingManager.getInstance(mContext),
+                mCarNotificationListener,
+                mCarUxRestrictionManagerWrapper
+        );
+        // Add to the SystemUI component registry
+        putComponent(NotificationsUI.class, this);
+    }
+
+    /**
+     * Connection callback to establish UX Restrictions
+     */
+    private ServiceConnection mCarConnectionListener = new ServiceConnection() {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            try {
+                mCarUxRestrictionsManager = (CarUxRestrictionsManager) mCar.getCarManager(
+                        Car.CAR_UX_RESTRICTION_SERVICE);
+                mCarUxRestrictionManagerWrapper
+                        .setCarUxRestrictionsManager(mCarUxRestrictionsManager);
+                PreprocessingManager preprocessingManager = PreprocessingManager.getInstance(
+                        mContext);
+                preprocessingManager
+                        .setCarUxRestrictionManagerWrapper(mCarUxRestrictionManagerWrapper);
+            } catch (CarNotConnectedException e) {
+                Log.e(TAG, "Car not connected in CarConnectionListener", e);
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            Log.e(TAG, "Car service disconnected unexpectedly");
+        }
+    };
+
+    /**
+     * Toggles the visiblity of the notifications
+     */
+    public void toggleShowingCarNotifications() {
+        if (mCarNotificationWindow.getVisibility() == View.VISIBLE) {
+            closeCarNotifications();
+            return;
+        }
+        openCarNotifications();
+    }
+
+    /**
+     * Hides the notifications
+     */
+    public void closeCarNotifications() {
+        mCarNotificationWindow.setVisibility(View.GONE);
+        mNotificationViewController.disable();
+        mIsShowing = false;
+    }
+
+    /**
+     * Sets the notifications to visible
+     */
+    public void openCarNotifications() {
+        mCarNotificationWindow.setVisibility(View.VISIBLE);
+        mNotificationViewController.enable();
+        mIsShowing = true;
+    }
+
+    /**
+     * Returns {@code true} if notifications are currently on the screen
+     */
+    public boolean isShowing() {
+        return mIsShowing;
+    }
+}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
index 81f7846..0cba351 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarView.java
@@ -21,7 +21,6 @@
 import android.view.View;
 import android.widget.LinearLayout;
 
-import com.android.keyguard.AlphaOptimizedImageButton;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -34,7 +33,7 @@
  */
 class CarNavigationBarView extends LinearLayout {
     private View mNavButtons;
-    private AlphaOptimizedImageButton mNotificationsButton;
+    private CarFacetButton mNotificationsButton;
     private CarStatusBar mCarStatusBar;
     private Context mContext;
     private View mLockScreenButtons;
@@ -71,7 +70,7 @@
     }
 
     protected void onNotificationsClick(View v) {
-        mCarStatusBar.togglePanel();
+        mCarStatusBar.toggleCarNotifications();
     }
 
     /**
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 2d90f8f..5da236c 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -35,6 +35,7 @@
 import com.android.systemui.classifier.FalsingLog;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.fragments.FragmentHostManager;
+import com.android.systemui.notifications.NotificationsUI;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.qs.car.CarQSFragment;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -587,4 +588,9 @@
     private Drawable getDefaultWallpaper() {
         return mContext.getDrawable(com.android.internal.R.drawable.default_wallpaper);
     }
+
+    public void toggleCarNotifications() {
+        getComponent(NotificationsUI.class).toggleShowingCarNotifications();
+    }
+
 }
diff --git a/packages/ExtServices/AndroidManifest.xml b/packages/ExtServices/AndroidManifest.xml
index ff70e97..010a810 100644
--- a/packages/ExtServices/AndroidManifest.xml
+++ b/packages/ExtServices/AndroidManifest.xml
@@ -63,6 +63,13 @@
                 android:resource="@array/autofill_field_classification_available_algorithms" />
         </service>
 
+        <service android:name=".sms.FinancialSmsServiceImpl"
+                 android:permission="android.permission.BIND_FINANCIAL_SMS_SERVICE">
+            <intent-filter>
+                <action android:name="android.service.sms.action.FINANCIAL_SERVICE_INTENT" />
+            </intent-filter>
+        </service>
+
         <library android:name="android.ext.services"/>
     </application>
 
diff --git a/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java b/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
new file mode 100644
index 0000000..ab718021
--- /dev/null
+++ b/packages/ExtServices/src/android/ext/services/sms/FinancialSmsServiceImpl.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 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.ext.services.sms;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.database.Cursor;
+import android.database.CursorWindow;
+import android.net.Uri;
+import android.os.Bundle;
+import android.service.sms.FinancialSmsService;
+import android.util.Log;
+
+import java.util.ArrayList;
+/**
+ * Service to provide financial apps access to sms messages.
+ */
+public class FinancialSmsServiceImpl extends FinancialSmsService {
+
+    private static final String TAG = "FinancialSmsServiceImpl";
+    private static final String KEY_COLUMN_NAMES = "column_names";
+
+    @Nullable
+    @Override
+    public CursorWindow onGetSmsMessages(@NonNull Bundle params) {
+        ArrayList<String> columnNames = params.getStringArrayList(KEY_COLUMN_NAMES);
+        if (columnNames == null || columnNames.size() <= 0) {
+            return null;
+        }
+
+        Uri inbox = Uri.parse("content://sms/inbox");
+
+        try (Cursor cursor = getContentResolver().query(inbox, null, null, null, null);
+                CursorWindow window = new CursorWindow("FinancialSmsMessages")) {
+            int messageCount = cursor.getCount();
+            if (messageCount > 0 && cursor.moveToFirst()) {
+                window.setNumColumns(columnNames.size());
+                for (int row = 0; row < messageCount; row++) {
+                    if (!window.allocRow()) {
+                        Log.e(TAG, "CursorWindow ran out of memory.");
+                        return null;
+                    }
+                    for (int col = 0; col < columnNames.size(); col++) {
+                        String columnName = columnNames.get(col);
+                        int inboxColumnIndex = cursor.getColumnIndexOrThrow(columnName);
+                        String inboxColumnValue = cursor.getString(inboxColumnIndex);
+                        boolean addedToCursorWindow = window.putString(inboxColumnValue, row, col);
+                        if (!addedToCursorWindow) {
+                            Log.e(TAG, "Failed to add:"
+                                    + inboxColumnValue
+                                    + ";column:"
+                                    + columnName);
+                            return null;
+                        }
+                    }
+                    cursor.moveToNext();
+                }
+            } else {
+                Log.w(TAG, "No sms messages.");
+            }
+            return window;
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to get sms messages.");
+            return null;
+        }
+    }
+}
diff --git a/packages/ExtServices/tests/src/android/ext/services/sms/FinancialSmsServiceImplTest.java b/packages/ExtServices/tests/src/android/ext/services/sms/FinancialSmsServiceImplTest.java
new file mode 100644
index 0000000..12575a6
--- /dev/null
+++ b/packages/ExtServices/tests/src/android/ext/services/sms/FinancialSmsServiceImplTest.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2018 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.ext.services.sms;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Bundle;
+
+import org.junit.Test;
+
+/**
+ * Contains the base tests for FinancialSmsServiceImpl.
+ */
+public class FinancialSmsServiceImplTest {
+
+    private final FinancialSmsServiceImpl mService = new FinancialSmsServiceImpl();
+
+    @Test
+    public void testOnGetSmsMessages_nullWithNoParamData() {
+        assertThat(mService.onGetSmsMessages(new Bundle())).isNull();
+    }
+}
diff --git a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
index 01d9c00..e27ae7d 100644
--- a/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
+++ b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
@@ -73,7 +73,7 @@
 
     <LinearLayout
         android:layout_width="wrap_content"
-        android:layout_height="match_parent"
+        android:layout_height="wrap_content"
         android:layout_centerVertical="true"
         android:layout_alignParentEnd="true"
         android:orientation="vertical">
diff --git a/packages/SystemUI/res/layout/qs_detail.xml b/packages/SystemUI/res/layout/qs_detail.xml
index 0b9a7b2..294bd50 100644
--- a/packages/SystemUI/res/layout/qs_detail.xml
+++ b/packages/SystemUI/res/layout/qs_detail.xml
@@ -24,7 +24,8 @@
     android:orientation="vertical"
     android:paddingBottom="8dp"
     android:visibility="invisible"
-    android:elevation="4dp" >
+    android:elevation="4dp"
+    android:importantForAccessibility="no" >
 
     <include
         android:id="@+id/qs_detail_header"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 3851190..6037dfc 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -24,7 +24,7 @@
     <!-- Minimum swipe distance to catch the swipe gestures to invoke assist or switch tasks. -->
     <dimen name="navigation_bar_min_swipe_distance">48dp</dimen>
     <!-- The distance from a side of device of the navigation bar to start an edge swipe -->
-    <dimen name="navigation_bar_edge_swipe_threshold">60dp</dimen>
+    <dimen name="navigation_bar_edge_swipe_threshold">48dp</dimen>
 
     <!-- thickness (height) of the dead zone at the top of the navigation bar,
          reducing false presses on navbar buttons; approx 2mm -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index fede934..87155c4 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -144,7 +144,7 @@
         <item name="android:textSize">@dimen/qs_time_expanded_size</item>
         <item name="android:textStyle">normal</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
-        <item name="android:fontFamily">sans-serif</item>
+        <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
     </style>
 
     <style name="TextAppearance.StatusBar.Expanded.AboveDateTime">
@@ -171,12 +171,12 @@
     <style name="TextAppearance.QS">
         <item name="android:textStyle">normal</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
-        <item name="android:fontFamily">sans-serif</item>
+        <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
     </style>
 
     <style name="TextAppearance.QS.DetailHeader">
         <item name="android:textSize">@dimen/qs_detail_header_text_size</item>
-        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
     </style>
 
     <style name="TextAppearance.QS.DetailItemPrimary">
@@ -202,7 +202,7 @@
         <item name="android:textSize">@dimen/qs_detail_button_text_size</item>
         <item name="android:textColor">?android:attr/textColorSecondary</item>
         <item name="android:textAllCaps">true</item>
-        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
         <item name="android:gravity">center</item>
     </style>
 
@@ -222,7 +222,7 @@
 
     <style name="TextAppearance.QS.SegmentedButton">
         <item name="android:textSize">16sp</item>
-        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
     </style>
 
     <style name="TextAppearance.QS.DataUsage">
@@ -245,7 +245,7 @@
 
     <style name="TextAppearance.QS.TileLabel.Secondary">
         <item name="android:textSize">@dimen/qs_tile_text_size</item>
-        <item name="android:fontFamily">sans-serif</item>
+        <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
     </style>
 
     <style name="TextAppearance.QS.CarrierInfo">
@@ -262,7 +262,7 @@
 
     <style name="TextAppearance.AppOpsDialog.Item">
         <item name="android:textSize">@dimen/ongoing_appops_dialog_item_size</item>
-        <item name="android:fontFamily">sans-serif</item>
+        <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
     </style>
 
     <style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI">
@@ -391,7 +391,7 @@
     <style name="TextAppearance.Volume">
         <item name="android:textStyle">normal</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
-        <item name="android:fontFamily">sans-serif</item>
+        <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
     </style>
 
     <style name="TextAppearance.Volume.Header">
@@ -435,7 +435,7 @@
     </style>
 
     <style name="TextAppearance.NotificationInfo">
-        <item name="android:fontFamily">sans-serif</item>
+        <item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
         <item name="android:textColor">@color/notification_primary_text_color</item>
     </style>
 
@@ -463,7 +463,7 @@
     </style>
 
     <style name="TextAppearance.NotificationInfo.Button">
-        <item name="android:fontFamily">sans-serif-medium</item>
+        <item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
         <item name="android:textSize">14sp</item>
         <item name="android:textColor">?android:attr/colorAccent</item>
         <item name="android:background">@drawable/btn_borderless_rect</item>
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
index 9aa21f8..69efbc8 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchState.java
@@ -84,7 +84,7 @@
      * Processes a given touch event and updates the state.
      */
     public void onTouchEvent(MotionEvent ev) {
-        switch (ev.getAction()) {
+        switch (ev.getActionMasked()) {
             case MotionEvent.ACTION_DOWN: {
                 if (!mAllowTouches) {
                     return;
@@ -92,12 +92,13 @@
 
                 // Initialize the velocity tracker
                 initOrResetVelocityTracker();
+                addMovement(ev);
 
                 mActivePointerId = ev.getPointerId(0);
                 if (DEBUG) {
                     Log.e(TAG, "Setting active pointer id on DOWN: " + mActivePointerId);
                 }
-                mLastTouch.set(ev.getX(), ev.getY());
+                mLastTouch.set(ev.getRawX(), ev.getRawY());
                 mDownTouch.set(mLastTouch);
                 mAllowDraggingOffscreen = true;
                 mIsUserInteracting = true;
@@ -118,15 +119,15 @@
                 }
 
                 // Update the velocity tracker
-                mVelocityTracker.addMovement(ev);
+                addMovement(ev);
                 int pointerIndex = ev.findPointerIndex(mActivePointerId);
                 if (pointerIndex == -1) {
                     Log.e(TAG, "Invalid active pointer id on MOVE: " + mActivePointerId);
                     break;
                 }
 
-                float x = ev.getX(pointerIndex);
-                float y = ev.getY(pointerIndex);
+                float x = ev.getRawX(pointerIndex);
+                float y = ev.getRawY(pointerIndex);
                 mLastDelta.set(x - mLastTouch.x, y - mLastTouch.y);
                 mDownDelta.set(x - mDownTouch.x, y - mDownTouch.y);
 
@@ -149,7 +150,7 @@
                 }
 
                 // Update the velocity tracker
-                mVelocityTracker.addMovement(ev);
+                addMovement(ev);
 
                 int pointerIndex = ev.getActionIndex();
                 int pointerId = ev.getPointerId(pointerIndex);
@@ -161,7 +162,7 @@
                         Log.e(TAG, "Relinquish active pointer id on POINTER_UP: " +
                                 mActivePointerId);
                     }
-                    mLastTouch.set(ev.getX(newPointerIndex), ev.getY(newPointerIndex));
+                    mLastTouch.set(ev.getRawX(newPointerIndex), ev.getRawY(newPointerIndex));
                 }
                 break;
             }
@@ -172,7 +173,7 @@
                 }
 
                 // Update the velocity tracker
-                mVelocityTracker.addMovement(ev);
+                addMovement(ev);
                 mVelocityTracker.computeCurrentVelocity(1000,
                         mViewConfig.getScaledMaximumFlingVelocity());
                 mVelocity.set(mVelocityTracker.getXVelocity(), mVelocityTracker.getYVelocity());
@@ -184,7 +185,7 @@
                 }
 
                 mUpTouchTime = ev.getEventTime();
-                mLastTouch.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
+                mLastTouch.set(ev.getRawX(pointerIndex), ev.getRawY(pointerIndex));
                 mPreviouslyDragging = mIsDragging;
                 mIsWaitingForDoubleTap = !mIsDoubleTap && !mIsDragging &&
                         (mUpTouchTime - mDownTouchTime) < DOUBLE_TAP_TIMEOUT;
@@ -331,6 +332,16 @@
         }
     }
 
+    private void addMovement(MotionEvent event) {
+        // Add movement to velocity tracker using raw screen X and Y coordinates instead
+        // of window coordinates because the window frame may be moving at the same time.
+        float deltaX = event.getRawX() - event.getX();
+        float deltaY = event.getRawY() - event.getY();
+        event.offsetLocation(deltaX, deltaY);
+        mVelocityTracker.addMovement(event);
+        event.offsetLocation(-deltaX, -deltaY);
+    }
+
     public void dump(PrintWriter pw, String prefix) {
         final String innerPrefix = prefix + "  ";
         pw.println(prefix + TAG);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
index 32fd2dc..bfcf021 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
+import android.graphics.Color;
 import android.graphics.Path;
 import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.Drawable;
@@ -88,6 +89,7 @@
         float pathSize = AdaptiveIconDrawable.MASK_SIZE;
         PathShape p = new PathShape(path, pathSize, pathSize);
         ShapeDrawable d = new ShapeDrawable(p);
+        d.setTintList(ColorStateList.valueOf(Color.TRANSPARENT));
         int bgSize = context.getResources().getDimensionPixelSize(R.dimen.qs_tile_background_size);
         d.setIntrinsicHeight(bgSize);
         d.setIntrinsicWidth(bgSize);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 6cec36a..91b34fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -916,6 +916,10 @@
         updateRelativeOffset();
     }
 
+    public void onUiModeChanged() {
+        updateBackgroundColors();
+    }
+
     private class ShelfState extends ExpandableViewState {
         private float openedAmount;
         private boolean hasItemsInStableShelf;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 7876b24..1d79152 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -225,7 +225,7 @@
         initDimens();
     }
 
-    public void onUiModeChanged() {
+    protected void updateBackgroundColors() {
         updateColors();
         initBackground();
         updateBackgroundTint();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 8bed366..383446c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -121,6 +121,7 @@
     private static final int MENU_VIEW_INDEX = 0;
     private static final String TAG = "ExpandableNotifRow";
     public static final float DEFAULT_HEADER_VISIBLE_AMOUNT = 1.0f;
+    private boolean mUpdateBackgroundOnUpdate;
 
     /**
      * Listener for when {@link ExpandableNotificationRow} is laid out.
@@ -587,6 +588,10 @@
         updateIconVisibilities();
         updateShelfIconColor();
         updateRippleAllowed();
+        if (mUpdateBackgroundOnUpdate) {
+            mUpdateBackgroundOnUpdate = false;
+            updateBackgroundColors();
+        }
     }
 
     /** Called when the notification's ranking was changed (but nothing else changed). */
@@ -1212,9 +1217,8 @@
         }
     }
 
-    @Override
     public void onUiModeChanged() {
-        super.onUiModeChanged();
+        mUpdateBackgroundOnUpdate = true;
         reInflateViews();
         if (mChildrenContainer != null) {
             for (ExpandableNotificationRow child : mChildrenContainer.getNotificationChildren()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index eca1a14..dbe6e8e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -641,15 +641,7 @@
     public void onUiModeChanged() {
         mBgColor = mContext.getColor(R.color.notification_shade_background_color);
         updateBackgroundDimming();
-
-        // Re-inflate all notification views
-        int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            View child = getChildAt(i);
-            if (child instanceof ActivatableNotificationView) {
-                ((ActivatableNotificationView) child).onUiModeChanged();
-            }
-        }
+        mShelf.onUiModeChanged();
     }
 
     @ShadeViewRefactor(RefactorComponent.DECORATOR)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index a2a11bb..c7e4d34 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -105,10 +105,22 @@
 
     private static final boolean DEBUG = false;
 
-    private static final boolean EXPAND_ON_WAKE_UP = SystemProperties.getBoolean(
+    /**
+     * If passive interrupts expand the NSSL or not
+     */
+    private static final boolean EXPAND_ON_PASSIVE_INTERRUPT = SystemProperties.getBoolean(
             "persist.sysui.expand_shade_on_wake_up", true);
+    /**
+     * If the notification panel should remain collapsed when the phone wakes up, even if the user
+     * presses power.
+     */
+    private static final boolean NEVER_EXPAND_WHEN_WAKING_UP = SystemProperties.getBoolean(
+            "persist.sysui.defer_notifications_on_lock_screen", false);
+    /**
+     * If waking up the phone should take you to SHADE_LOCKED instead of KEYGUARD
+     */
     private static final boolean WAKE_UP_TO_SHADE = SystemProperties.getBoolean(
-            "persist.sysui.go_to_shade_on_wake_up", true);
+            "persist.sysui.go_to_shade_on_wake_up", false);
 
     /**
      * Fling expanding QS.
@@ -2774,10 +2786,12 @@
     }
 
     public void setDozing(boolean dozing, boolean animate, PointF wakeUpTouchLocation,
-            boolean passiveInterrupted) {
+            boolean passivelyInterrupted) {
         if (dozing == mDozing) return;
         mDozing = dozing;
-        mSemiAwake = !EXPAND_ON_WAKE_UP && !mDozing && passiveInterrupted;
+        boolean doNotExpand = (!EXPAND_ON_PASSIVE_INTERRUPT && passivelyInterrupted)
+                || NEVER_EXPAND_WHEN_WAKING_UP;
+        mSemiAwake = doNotExpand && !mDozing;
         if (!mSemiAwake) {
             mNotificationStackScroller.setDark(mDozing, animate, wakeUpTouchLocation);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index acdd5e9..261f117 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -67,6 +67,8 @@
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardMonitor;
 
+import java.util.ArrayList;
+
 public class StatusBarNotificationPresenter implements NotificationPresenter,
         ConfigurationController.ConfigurationListener {
 
@@ -105,6 +107,7 @@
     private final int mMaxAllowedKeyguardNotifications;
     private final IStatusBarService mBarService;
     private boolean mReinflateNotificationsOnUserSwitched;
+    private boolean mDispatchUiModeChangeOnUserSwitched;
     private final UnlockMethodCache mUnlockMethodCache;
     private TextView mNotificationPanelDebugText;
 
@@ -187,6 +190,27 @@
     }
 
     @Override
+    public void onUiModeChanged() {
+        if (!KeyguardUpdateMonitor.getInstance(mContext).isSwitchingUser()) {
+            updateNotificationOnUiModeChanged();
+        } else {
+            mDispatchUiModeChangeOnUserSwitched = true;
+        }
+    }
+
+    private void updateNotificationOnUiModeChanged() {
+        ArrayList<Entry> userNotifications
+                = mEntryManager.getNotificationData().getNotificationsForCurrentUser();
+        for (int i = 0; i < userNotifications.size(); i++) {
+            Entry entry = userNotifications.get(i);
+            ExpandableNotificationRow row = entry.getRow();
+            if (row != null) {
+                row.onUiModeChanged();
+            }
+        }
+    }
+
+    @Override
     public boolean isCollapsing() {
         return mNotificationPanel.isCollapsing()
                 || mActivityLaunchAnimator.isAnimationPending()
@@ -301,6 +325,10 @@
             mEntryManager.updateNotificationsOnDensityOrFontScaleChanged();
             mReinflateNotificationsOnUserSwitched = false;
         }
+        if (mDispatchUiModeChangeOnUserSwitched) {
+            updateNotificationOnUiModeChanged();
+            mDispatchUiModeChangeOnUserSwitched = false;
+        }
         updateNotificationViews();
         mMediaManager.clearCurrentMediaNotification();
         mShadeController.setLockscreenUser(newUserId);
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 2d30640..eb0090b 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -6637,6 +6637,11 @@
     // OS: Q
     SETTINGS_WIFI_DPP_ENROLLEE = 1596;
 
+    // OPEN: Settings > Apps & Notifications -> Special app access -> Financial Apps Sms Access
+    // CATEGORY: SETTINGS
+    // OS: Q
+    SETTINGS_FINANCIAL_APPS_SMS_ACCESS = 1597;
+
     // ---- End Q Constants, all Q constants go above this line ----
 
     // Add new aosp constants above this line.
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index eda9fe1..89194e4 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1968,6 +1968,7 @@
     void systemReady() {
         mProxyTracker.loadGlobalProxy();
         registerNetdEventCallback();
+        mTethering.systemReady();
 
         synchronized (this) {
             mSystemReady = true;
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 4678fec..8869af4 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -164,8 +164,6 @@
 
     private static final int MAX_UID_RANGES_PER_COMMAND = 10;
 
-    private static final  String[] EMPTY_STRING_ARRAY = new String[0];
-
     /**
      * Name representing {@link #setGlobalAlert(long)} limit when delivered to
      * {@link INetworkManagementEventObserver#limitReached(String, String)}.
@@ -959,8 +957,7 @@
     public String[] listInterfaces() {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            final List<String> result = mNetdService.interfaceGetList();
-            return result.toArray(EMPTY_STRING_ARRAY);
+            return mNetdService.interfaceGetList();
         } catch (RemoteException | ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
@@ -1252,8 +1249,7 @@
     public String[] listTetheredInterfaces() {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            final List<String> result = mNetdService.tetherInterfaceList();
-            return result.toArray(EMPTY_STRING_ARRAY);
+            return mNetdService.tetherInterfaceList();
         } catch (RemoteException | ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
@@ -1276,8 +1272,7 @@
     public String[] getDnsForwarders() {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            final List<String> result = mNetdService.tetherDnsList();
-            return result.toArray(EMPTY_STRING_ARRAY);
+            return mNetdService.tetherDnsList();
         } catch (RemoteException | ServiceSpecificException e) {
             throw new IllegalStateException(e);
         }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 4b092b2..581d435 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -16,7 +16,15 @@
 
 package com.android.server;
 
-import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static android.Manifest.permission.INSTALL_PACKAGES;
+import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
+import static android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
 import static android.os.storage.OnObbStateChangeListener.ERROR_ALREADY_MOUNTED;
 import static android.os.storage.OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT;
@@ -27,9 +35,11 @@
 import static android.os.storage.OnObbStateChangeListener.MOUNTED;
 import static android.os.storage.OnObbStateChangeListener.UNMOUNTED;
 
+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.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;
@@ -51,7 +61,9 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
 import android.content.pm.IPackageMoveObserver;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ProviderInfo;
@@ -116,12 +128,14 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.IAppOpsService;
 import com.android.internal.os.AppFuseMount;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.FuseUnavailableMountException;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.os.Zygote;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.HexDump;
@@ -204,7 +218,9 @@
 
         @Override
         public void onBootPhase(int phase) {
-            if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+            if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+                mStorageManagerService.servicesReady();
+            } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
                 mStorageManagerService.systemReady();
             } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
                 mStorageManagerService.bootCompleted();
@@ -261,6 +277,7 @@
     private static final String TAG_VOLUMES = "volumes";
     private static final String ATTR_VERSION = "version";
     private static final String ATTR_PRIMARY_STORAGE_UUID = "primaryStorageUuid";
+    private static final String ATTR_ISOLATED_STORAGE = "isolatedStorage";
     private static final String TAG_VOLUME = "volume";
     private static final String ATTR_TYPE = "type";
     private static final String ATTR_FS_UUID = "fsUuid";
@@ -311,6 +328,10 @@
     @GuardedBy("mLock")
     private String mPrimaryStorageUuid;
 
+    /** Flag indicating isolated storage state of last boot */
+    @GuardedBy("mLock")
+    private boolean mLastIsolatedStorage = false;
+
     /** Map from disk ID to latches */
     @GuardedBy("mLock")
     private ArrayMap<String, CountDownLatch> mDiskScanLatches = new ArrayMap<>();
@@ -453,6 +474,9 @@
     private UserManagerInternal mUmInternal;
     private ActivityManagerInternal mAmInternal;
 
+    private IPackageManager mIPackageManager;
+    private IAppOpsService mIAppOpsService;
+
     private final Callbacks mCallbacks;
     private final LockPatternUtils mLockPatternUtils;
 
@@ -1565,11 +1589,83 @@
         }
     }
 
+    private void servicesReady() {
+        synchronized (mLock) {
+            final boolean thisIsolatedStorage = StorageManager.hasIsolatedStorage();
+            if (mLastIsolatedStorage == thisIsolatedStorage) {
+                // Nothing changed since last boot; keep rolling forward
+                return;
+            } else if (thisIsolatedStorage) {
+                // This boot enables isolated storage; apply legacy behavior
+                applyLegacyStorage();
+            }
+
+            // Always remember the new state we just booted with
+            writeSettingsLocked();
+        }
+    }
+
+    /**
+     * If we're enabling isolated storage, we need to remember which existing
+     * apps have already been using shared storage, and grant them legacy access
+     * to keep them running smoothly.
+     */
+    private void applyLegacyStorage() {
+        final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+        final UserManagerInternal um = LocalServices.getService(UserManagerInternal.class);
+        for (int userId : um.getUserIds()) {
+            final PackageManager pm;
+            try {
+                pm = mContext.createPackageContextAsUser(mContext.getPackageName(),
+                        0, UserHandle.of(userId)).getPackageManager();
+            } catch (PackageManager.NameNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+
+            final List<PackageInfo> pkgs = pm.getPackagesHoldingPermissions(new String[] {
+                    android.Manifest.permission.READ_EXTERNAL_STORAGE,
+                    android.Manifest.permission.WRITE_EXTERNAL_STORAGE
+            }, MATCH_UNINSTALLED_PACKAGES | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE);
+            for (PackageInfo pkg : pkgs) {
+                final int uid = pkg.applicationInfo.uid;
+                final String packageName = pkg.applicationInfo.packageName;
+
+                final long lastAccess = getLastAccessTime(appOps, uid, packageName, new int[] {
+                        AppOpsManager.OP_READ_EXTERNAL_STORAGE,
+                        AppOpsManager.OP_WRITE_EXTERNAL_STORAGE,
+                });
+
+                Log.d(TAG, "Found " + uid + " " + packageName
+                        + " with granted storage access, last accessed " + lastAccess);
+                if (lastAccess > 0) {
+                    appOps.setMode(AppOpsManager.OP_LEGACY_STORAGE,
+                            uid, packageName, AppOpsManager.MODE_ALLOWED);
+                }
+            }
+        }
+    }
+
+    private static long getLastAccessTime(AppOpsManager manager,
+            int uid, String packageName, int[] ops) {
+        long maxTime = 0;
+        final List<AppOpsManager.PackageOps> pkgs = manager.getOpsForPackage(uid, packageName, ops);
+        for (AppOpsManager.PackageOps pkg : CollectionUtils.defeatNullable(pkgs)) {
+            for (AppOpsManager.OpEntry op : CollectionUtils.defeatNullable(pkg.getOps())) {
+                maxTime = Math.max(maxTime, op.getLastAccessTime());
+            }
+        }
+        return maxTime;
+    }
+
     private void systemReady() {
         LocalServices.getService(ActivityTaskManagerInternal.class)
                 .registerScreenObserver(this);
 
         mSystemReady = true;
+        mIPackageManager = IPackageManager.Stub.asInterface(
+                ServiceManager.getService("package"));
+        mIAppOpsService = IAppOpsService.Stub.asInterface(
+                ServiceManager.getService(Context.APP_OPS_SERVICE));
         mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
     }
 
@@ -1589,6 +1685,7 @@
     private void readSettingsLocked() {
         mRecords.clear();
         mPrimaryStorageUuid = getDefaultPrimaryStorageUuid();
+        mLastIsolatedStorage = false;
 
         FileInputStream fis = null;
         try {
@@ -1610,6 +1707,8 @@
                             mPrimaryStorageUuid = readStringAttribute(in,
                                     ATTR_PRIMARY_STORAGE_UUID);
                         }
+                        mLastIsolatedStorage = readBooleanAttribute(in,
+                                ATTR_ISOLATED_STORAGE, false);
 
                     } else if (TAG_VOLUME.equals(tag)) {
                         final VolumeRecord rec = readVolumeRecord(in);
@@ -1640,6 +1739,7 @@
             out.startTag(null, TAG_VOLUMES);
             writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY);
             writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid);
+            writeBooleanAttribute(out, ATTR_ISOLATED_STORAGE, StorageManager.hasIsolatedStorage());
             final int size = mRecords.size();
             for (int i = 0; i < size; i++) {
                 final VolumeRecord rec = mRecords.valueAt(i);
@@ -2775,11 +2875,7 @@
         Slog.v(TAG, "mountProxyFileDescriptor");
 
         // We only support a narrow set of incoming mode flags
-        if ((mode & MODE_READ_WRITE) == MODE_READ_WRITE) {
-            mode = MODE_READ_WRITE;
-        } else {
-            mode = MODE_READ_ONLY;
-        }
+        mode &= MODE_READ_WRITE;
 
         try {
             synchronized (mAppFuseLock) {
@@ -2906,7 +3002,7 @@
         }
 
         if (!foundPrimary) {
-            Log.w(TAG, "No primary storage defined yet; hacking together a stub");
+            Slog.w(TAG, "No primary storage defined yet; hacking together a stub");
 
             final boolean primaryPhysical = SystemProperties.getBoolean(
                     StorageManager.PROP_PRIMARY_PHYSICAL, false);
@@ -3117,7 +3213,8 @@
             throw new SecurityException("Shady looking path " + path);
         }
 
-        if (!mAmInternal.isAppStorageSandboxed(pid, uid)) {
+        final int mountMode = mAmInternal.getStorageMountMode(pid, uid);
+        if (mountMode == Zygote.MOUNT_EXTERNAL_FULL) {
             return path;
         }
 
@@ -3126,6 +3223,11 @@
             final String device = m.group(1);
             final String devicePath = m.group(2);
 
+            if (mountMode == Zygote.MOUNT_EXTERNAL_INSTALLER
+                    && devicePath.startsWith("Android/obb/")) {
+                return path;
+            }
+
             // Does path belong to any packages belonging to this UID? If so,
             // they get to go straight through to legacy paths.
             final String[] pkgs = mContext.getPackageManager().getPackagesForUid(uid);
@@ -3477,6 +3579,31 @@
         }
     }
 
+    private int getMountMode(int uid, String packageName) {
+        try {
+            if (Process.isIsolated(uid)) {
+                return Zygote.MOUNT_EXTERNAL_NONE;
+            }
+            if (mIPackageManager.checkUidPermission(WRITE_MEDIA_STORAGE, uid)
+                    == PERMISSION_GRANTED) {
+                return Zygote.MOUNT_EXTERNAL_FULL;
+            } else if (mIAppOpsService.checkOperation(OP_LEGACY_STORAGE, uid,
+                    packageName) == MODE_ALLOWED) {
+                // TODO: define a specific "legacy" mount mode
+                return Zygote.MOUNT_EXTERNAL_FULL;
+            } else if (mIPackageManager.checkUidPermission(INSTALL_PACKAGES, uid)
+                    == PERMISSION_GRANTED || mIAppOpsService.checkOperation(
+                            OP_REQUEST_INSTALL_PACKAGES, uid, packageName) == MODE_ALLOWED) {
+                return Zygote.MOUNT_EXTERNAL_INSTALLER;
+            } else {
+                return Zygote.MOUNT_EXTERNAL_WRITE;
+            }
+        } catch (RemoteException e) {
+            // Should not happen
+        }
+        return Zygote.MOUNT_EXTERNAL_NONE;
+    }
+
     private static class Callbacks extends Handler {
         private static final int MSG_STORAGE_STATE_CHANGED = 1;
         private static final int MSG_VOLUME_STATE_CHANGED = 2;
@@ -3718,6 +3845,9 @@
 
         @Override
         public int getExternalStorageMountMode(int uid, String packageName) {
+            if (ENABLE_ISOLATED_STORAGE) {
+                return getMountMode(uid, packageName);
+            }
             // No locking - CopyOnWriteArrayList
             int mountMode = Integer.MAX_VALUE;
             for (ExternalStorageMountPolicy policy : mPolicies) {
@@ -3754,6 +3884,9 @@
             if (uid == Process.SYSTEM_UID) {
                 return true;
             }
+            if (ENABLE_ISOLATED_STORAGE) {
+                return getMountMode(uid, packageName) != Zygote.MOUNT_EXTERNAL_NONE;
+            }
             // No locking - CopyOnWriteArrayList
             for (ExternalStorageMountPolicy policy : mPolicies) {
                 final boolean policyHasStorage = policy.hasExternalStorage(uid, packageName);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index f7acf7e..8751d24 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2019,6 +2019,13 @@
                 ComponentName className = new ComponentName(
                         sInfo.applicationInfo.packageName, sInfo.name);
                 ComponentName name = comp != null ? comp : className;
+                if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid,
+                        name.getPackageName(), sInfo.applicationInfo.uid)) {
+                    String msg = "association not allowed between packages "
+                            + callingPackage + " and " + r.packageName;
+                    Slog.w(TAG, "Service lookup failed: " + msg);
+                    return new ServiceLookupResult(null, msg);
+                }
                 if ((sInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) != 0) {
                     if (isBindExternal) {
                         if (!sInfo.exported) {
@@ -2099,6 +2106,17 @@
             }
         }
         if (r != null) {
+            if (!mAm.validateAssociationAllowedLocked(callingPackage, callingUid, r.packageName,
+                    r.appInfo.uid)) {
+                String msg = "association not allowed between packages "
+                        + callingPackage + " and " + r.packageName;
+                Slog.w(TAG, "Service lookup failed: " + msg);
+                return new ServiceLookupResult(null, msg);
+            }
+            if (!mAm.mIntentFirewall.checkService(r.name, service, callingUid, callingPid,
+                    resolvedType, r.appInfo)) {
+                return new ServiceLookupResult(null, "blocked by firewall");
+            }
             if (mAm.checkComponentPermission(r.permission,
                     callingPid, callingUid, r.appInfo.uid, r.exported) != PERMISSION_GRANTED) {
                 if (!r.exported) {
@@ -2125,11 +2143,6 @@
                     return null;
                 }
             }
-
-            if (!mAm.mIntentFirewall.checkService(r.name, service, callingUid, callingPid,
-                    resolvedType, r.appInfo)) {
-                return null;
-            }
             return new ServiceLookupResult(r, null);
         }
         return null;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 69cfcc2..6700a53 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -658,6 +658,12 @@
     ArraySet<String> mBackgroundLaunchBroadcasts;
 
     /**
+     * When an app has restrictions on the other apps that can have associations with it,
+     * it appears here with a set of the allowed apps.
+     */
+    ArrayMap<String, ArraySet<String>> mAllowedAssociations;
+
+    /**
      * All of the processes we currently have running organized by pid.
      * The keys are the pid running the application.
      *
@@ -2396,6 +2402,34 @@
         return mBackgroundLaunchBroadcasts;
     }
 
+    boolean validateAssociationAllowedLocked(String pkg1, int uid1, String pkg2, int uid2) {
+        if (mAllowedAssociations == null) {
+            mAllowedAssociations = SystemConfig.getInstance().getAllowedAssociations();
+        }
+        // Interactions with the system uid are always allowed, since that is the core system
+        // that everyone needs to be able to interact with.
+        if (UserHandle.getAppId(uid1) == SYSTEM_UID) {
+            return true;
+        }
+        if (UserHandle.getAppId(uid2) == SYSTEM_UID) {
+            return true;
+        }
+        // We won't allow this association if either pkg1 or pkg2 has a limit on the
+        // associations that are allowed with it, and the other package is not explicitly
+        // specified as one of those associations.
+        ArraySet<String> pkgs = mAllowedAssociations.get(pkg1);
+        if (pkgs != null) {
+            if (!pkgs.contains(pkg2)) {
+                return false;
+            }
+        }
+        pkgs = mAllowedAssociations.get(pkg2);
+        if (pkgs != null) {
+            return pkgs.contains(pkg1);
+        }
+        return true;
+    }
+
     @Override
     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
             throws RemoteException {
@@ -6264,6 +6298,21 @@
         return state != 'Z' && state != 'X' && state != 'x' && state != 'K';
     }
 
+    private String checkContentProviderAssociation(ProcessRecord callingApp, int callingUid,
+            ProviderInfo cpi) {
+        if (callingApp == null) {
+            return validateAssociationAllowedLocked(cpi.packageName, cpi.applicationInfo.uid,
+                    null, callingUid) ? null : "<null>";
+        }
+        for (int i = callingApp.pkgList.size() - 1; i >= 0; i--) {
+            if (!validateAssociationAllowedLocked(callingApp.pkgList.keyAt(i), callingApp.uid,
+                    cpi.packageName, cpi.applicationInfo.uid)) {
+                return cpi.packageName;
+            }
+        }
+        return null;
+    }
+
     private ContentProviderHolder getContentProviderImpl(IApplicationThread caller,
             String name, IBinder token, int callingUid, String callingTag, boolean stable,
             int userId) {
@@ -6335,6 +6384,11 @@
                 String msg;
 
                 if (r != null && cpr.canRunHere(r)) {
+                    if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
+                        throw new SecurityException("Content provider lookup "
+                                + cpr.name.flattenToShortString()
+                                + " failed: association not allowed with package " + msg);
+                    }
                     checkTime(startTime,
                             "getContentProviderImpl: before checkContentProviderPermission");
                     if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
@@ -6364,6 +6418,11 @@
                 } catch (RemoteException e) {
                 }
 
+                if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
+                    throw new SecurityException("Content provider lookup "
+                            + cpr.name.flattenToShortString()
+                            + " failed: association not allowed with package " + msg);
+                }
                 checkTime(startTime,
                         "getContentProviderImpl: before checkContentProviderPermission");
                 if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser))
@@ -6461,6 +6520,11 @@
                 checkTime(startTime, "getContentProviderImpl: got app info for user");
 
                 String msg;
+                if ((msg = checkContentProviderAssociation(r, callingUid, cpi)) != null) {
+                    throw new SecurityException("Content provider lookup "
+                            + cpr.name.flattenToShortString()
+                            + " failed: association not allowed with package " + msg);
+                }
                 checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission");
                 if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton))
                         != null) {
@@ -9207,6 +9271,12 @@
                 pw.println("-------------------------------------------------------------------------------");
 
             }
+            dumpAllowedAssociationsLocked(fd, pw, args, opti, dumpAll, dumpPackage);
+            pw.println();
+            if (dumpAll) {
+                pw.println("-------------------------------------------------------------------------------");
+
+            }
             mPendingIntentController.dumpPendingIntents(pw, dumpAll, dumpPackage);
             pw.println();
             if (dumpAll) {
@@ -9474,6 +9544,14 @@
                     System.gc();
                     pw.println(BinderInternal.nGetBinderProxyCount(Integer.parseInt(uid)));
                 }
+            } else if ("allowed-associations".equals(cmd)) {
+                if (opti < args.length) {
+                    dumpPackage = args[opti];
+                    opti++;
+                }
+                synchronized (this) {
+                    dumpAllowedAssociationsLocked(fd, pw, args, opti, true, dumpPackage);
+                }
             } else if ("broadcasts".equals(cmd) || "b".equals(cmd)) {
                 if (opti < args.length) {
                     dumpPackage = args[opti];
@@ -10824,6 +10902,44 @@
         proto.end(handlerToken);
     }
 
+    void dumpAllowedAssociationsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
+            int opti, boolean dumpAll, String dumpPackage) {
+        boolean needSep = false;
+        boolean printedAnything = false;
+
+        pw.println("ACTIVITY MANAGER ALLOWED ASSOCIATION STATE (dumpsys activity allowed-associations)");
+        boolean printed = false;
+        if (mAllowedAssociations != null) {
+            for (int i = 0; i < mAllowedAssociations.size(); i++) {
+                final String pkg = mAllowedAssociations.keyAt(i);
+                final ArraySet<String> asc = mAllowedAssociations.valueAt(i);
+                boolean printedHeader = false;
+                for (int j = 0; j < asc.size(); j++) {
+                    if (dumpPackage == null || pkg.equals(dumpPackage)
+                            || asc.valueAt(j).equals(dumpPackage)) {
+                        if (!printed) {
+                            pw.println("  Allowed associations (by restricted package):");
+                            printed = true;
+                            needSep = true;
+                            printedAnything = true;
+                        }
+                        if (!printedHeader) {
+                            pw.print("  * ");
+                            pw.print(pkg);
+                            pw.println(":");
+                            printedHeader = true;
+                        }
+                        pw.print("      Allow: ");
+                        pw.println(asc.valueAt(j));
+                    }
+                }
+            }
+        }
+        if (!printed) {
+            pw.println("  (No association restrictions)");
+        }
+    }
+
     void dumpBroadcastsLocked(FileDescriptor fd, PrintWriter pw, String[] args,
             int opti, boolean dumpAll, String dumpPackage) {
         boolean needSep = false;
@@ -19441,16 +19557,13 @@
         }
 
         @Override
-        public boolean isAppStorageSandboxed(int pid, int uid) {
-            if (!StorageManager.hasIsolatedStorage()) {
-                return false;
-            }
+        public int getStorageMountMode(int pid, int uid) {
             if (uid == SHELL_UID || uid == ROOT_UID) {
-                return false;
+                return Zygote.MOUNT_EXTERNAL_FULL;
             }
             synchronized (mPidsSelfLocked) {
                 final ProcessRecord pr = mPidsSelfLocked.get(pid);
-                return pr == null || pr.mountMode != Zygote.MOUNT_EXTERNAL_FULL;
+                return pr == null ? Zygote.MOUNT_EXTERNAL_NONE : pr.mountMode;
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 67a4d14..740c0da 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -20,6 +20,7 @@
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM;
 import static android.app.ActivityTaskManager.RESIZE_MODE_USER;
+import static android.app.WaitResult.launchStateToString;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.Display.INVALID_DISPLAY;
@@ -491,6 +492,7 @@
             final long endTime = SystemClock.uptimeMillis();
             PrintWriter out = mWaitOption ? pw : getErrPrintWriter();
             boolean launched = false;
+            boolean hotLaunch = false;
             switch (res) {
                 case ActivityManager.START_SUCCESS:
                     launched = true;
@@ -516,6 +518,8 @@
                     break;
                 case ActivityManager.START_TASK_TO_FRONT:
                     launched = true;
+                    //TODO(b/120981435) remove special case
+                    hotLaunch = true;
                     out.println(
                             "Warning: Activity not started, its current "
                                     + "task has been brought to the front");
@@ -563,6 +567,9 @@
                     result.who = intent.getComponent();
                 }
                 pw.println("Status: " + (result.timeout ? "timeout" : "ok"));
+                final @WaitResult.LaunchState int launchState =
+                        hotLaunch ? WaitResult.LAUNCH_STATE_HOT : result.launchState;
+                pw.println("LaunchState: " + launchStateToString(launchState));
                 if (result.who != null) {
                     pw.println("Activity: " + result.who.flattenToShortString());
                 }
@@ -2852,6 +2859,7 @@
             pw.println("    prov[iders] [COMP_SPEC ...]: content provider state");
             pw.println("    provider [COMP_SPEC]: provider client-side state");
             pw.println("    s[ervices] [COMP_SPEC ...]: service state");
+            pw.println("    allowed-associations: current package association restrictions");
             pw.println("    as[sociations]: tracked app associations");
             pw.println("    lmk: stats on low memory killer");
             pw.println("    lru: raw LRU process list");
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index dd3f3b5..1c1daff 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -18,6 +18,7 @@
 
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
 import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
+
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityManagerService.MY_PID;
@@ -27,7 +28,6 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
-import android.app.AppOpsManager;
 import android.app.ApplicationErrorReport;
 import android.app.Dialog;
 import android.content.ActivityNotFoundException;
@@ -835,15 +835,6 @@
                 return;
             }
 
-            Intent intent = new Intent("android.intent.action.ANR");
-            if (!mService.mProcessesReady) {
-                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
-                        | Intent.FLAG_RECEIVER_FOREGROUND);
-            }
-            mService.broadcastIntentLocked(null, null, intent,
-                    null, null, 0, null, null, null, AppOpsManager.OP_NONE,
-                    null, false, false, MY_PID, Process.SYSTEM_UID, 0 /* TODO: Verify */);
-
             boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),
                     Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;
             if (mService.mAtmInternal.canShowErrorDialogs() || showBackground) {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 3a0899d..c290fbe 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -527,6 +527,24 @@
     private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
             BroadcastFilter filter, boolean ordered, int index) {
         boolean skip = false;
+        if (!mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid,
+                filter.packageName, filter.owningUid)) {
+            Slog.w(TAG, "Association not allowed: broadcasting "
+                    + r.intent.toString()
+                    + " from " + r.callerPackage + " (pid=" + r.callingPid
+                    + ", uid=" + r.callingUid + ") to " + filter.packageName + " through "
+                    + filter);
+            skip = true;
+        }
+        if (!skip && !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
+                r.callingPid, r.resolvedType, filter.receiverList.uid)) {
+            Slog.w(TAG, "Firewall blocked: broadcasting "
+                    + r.intent.toString()
+                    + " from " + r.callerPackage + " (pid=" + r.callingPid
+                    + ", uid=" + r.callingUid + ") to " + filter.packageName + " through "
+                    + filter);
+            skip = true;
+        }
         if (filter.requiredPermission != null) {
             int perm = mService.checkComponentPermission(filter.requiredPermission,
                     r.callingPid, r.callingUid, -1, true);
@@ -619,11 +637,6 @@
             skip = true;
         }
 
-        if (!mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
-                r.callingPid, r.resolvedType, filter.receiverList.uid)) {
-            skip = true;
-        }
-
         if (!skip && (filter.receiverList.app == null || filter.receiverList.app.killed
                 || filter.receiverList.app.isCrashing())) {
             Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r
@@ -1082,6 +1095,24 @@
                         > brOptions.getMaxManifestReceiverApiLevel())) {
             skip = true;
         }
+        if (!skip && !mService.validateAssociationAllowedLocked(r.callerPackage, r.callingUid,
+                component.getPackageName(), info.activityInfo.applicationInfo.uid)) {
+            Slog.w(TAG, "Association not allowed: broadcasting "
+                    + r.intent.toString()
+                    + " from " + r.callerPackage + " (pid=" + r.callingPid
+                    + ", uid=" + r.callingUid + ") to " + component.flattenToShortString());
+            skip = true;
+        }
+        if (!skip) {
+            skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
+                    r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
+            if (skip) {
+                Slog.w(TAG, "Firewall blocked: broadcasting "
+                        + r.intent.toString()
+                        + " from " + r.callerPackage + " (pid=" + r.callingPid
+                        + ", uid=" + r.callingUid + ") to " + component.flattenToShortString());
+            }
+        }
         int perm = mService.checkComponentPermission(info.activityInfo.permission,
                 r.callingPid, r.callingUid, info.activityInfo.applicationInfo.uid,
                 info.activityInfo.exported);
@@ -1170,10 +1201,6 @@
                     + " (uid " + r.callingUid + ")");
             skip = true;
         }
-        if (!skip) {
-            skip = !mService.mIntentFirewall.checkBroadcast(r.intent, r.callingUid,
-                    r.callingPid, r.resolvedType, info.activityInfo.applicationInfo.uid);
-        }
         boolean isSingleton = false;
         try {
             isSingleton = mService.isSingleton(info.activityInfo.processName,
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index d75601b..9dfdddb 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -1382,7 +1382,7 @@
                     return;
                 }
 
-                mUpstreamNetworkMonitor.start(mDeps.getDefaultNetworkRequest());
+                mUpstreamNetworkMonitor.startObserveAllNetworks();
 
                 // TODO: De-duplicate with updateUpstreamWanted() below.
                 if (upstreamWanted()) {
@@ -1658,6 +1658,10 @@
         }
     }
 
+    public void systemReady() {
+        mUpstreamNetworkMonitor.startTrackDefaultNetwork(mDeps.getDefaultNetworkRequest());
+    }
+
     @Override
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         // Binder.java closes the resource for us.
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 3e5d5aa..3ac311b 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -55,10 +55,13 @@
  * A class to centralize all the network and link properties information
  * pertaining to the current and any potential upstream network.
  *
- * Calling #start() registers two callbacks: one to track the system default
- * network and a second to observe all networks.  The latter is necessary
- * while the expression of preferred upstreams remains a list of legacy
- * connectivity types.  In future, this can be revisited.
+ * The owner of UNM gets it to register network callbacks by calling the
+ * following methods :
+ * Calling #startTrackDefaultNetwork() to track the system default network.
+ * Calling #startObserveAllNetworks() to observe all networks. Listening all
+ * networks is necessary while the expression of preferred upstreams remains
+ * a list of legacy connectivity types.  In future, this can be revisited.
+ * Calling #registerMobileNetworkRequest() to bring up mobile DUN/HIPRI network.
  *
  * The methods and data members of this class are only to be accessed and
  * modified from the tethering master state machine thread. Any other
@@ -119,33 +122,31 @@
         mCM = cm;
     }
 
-    public void start(NetworkRequest defaultNetworkRequest) {
-        stop();
-
-        final NetworkRequest listenAllRequest = new NetworkRequest.Builder()
-                .clearCapabilities().build();
-        mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL);
-        cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler);
-
-        if (defaultNetworkRequest != null) {
-            // This is not really a "request", just a way of tracking the system default network.
-            // It's guaranteed not to actually bring up any networks because it's the same request
-            // as the ConnectivityService default request, and thus shares fate with it. We can't
-            // use registerDefaultNetworkCallback because it will not track the system default
-            // network if there is a VPN that applies to our UID.
+    public void startTrackDefaultNetwork(NetworkRequest defaultNetworkRequest) {
+        // This is not really a "request", just a way of tracking the system default network.
+        // It's guaranteed not to actually bring up any networks because it's the same request
+        // as the ConnectivityService default request, and thus shares fate with it. We can't
+        // use registerDefaultNetworkCallback because it will not track the system default
+        // network if there is a VPN that applies to our UID.
+        if (mDefaultNetworkCallback == null) {
             final NetworkRequest trackDefaultRequest = new NetworkRequest(defaultNetworkRequest);
             mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_DEFAULT_INTERNET);
             cm().requestNetwork(trackDefaultRequest, mDefaultNetworkCallback, mHandler);
         }
     }
 
+    public void startObserveAllNetworks() {
+        stop();
+
+        final NetworkRequest listenAllRequest = new NetworkRequest.Builder()
+                .clearCapabilities().build();
+        mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL);
+        cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler);
+    }
+
     public void stop() {
         releaseMobileNetworkRequest();
 
-        releaseCallback(mDefaultNetworkCallback);
-        mDefaultNetworkCallback = null;
-        mDefaultInternetNetwork = null;
-
         releaseCallback(mListenAllCallback);
         mListenAllCallback = null;
 
@@ -264,9 +265,7 @@
         mNetworkMap.put(network, new NetworkState(null, null, null, network, null, null));
     }
 
-    private void handleNetCap(int callbackType, Network network, NetworkCapabilities newNc) {
-        if (callbackType == CALLBACK_DEFAULT_INTERNET) mDefaultInternetNetwork = network;
-
+    private void handleNetCap(Network network, NetworkCapabilities newNc) {
         final NetworkState prev = mNetworkMap.get(network);
         if (prev == null || newNc.equals(prev.networkCapabilities)) {
             // Ignore notifications about networks for which we have not yet
@@ -315,31 +314,25 @@
         notifyTarget(EVENT_ON_LINKPROPERTIES, network);
     }
 
-    private void handleSuspended(int callbackType, Network network) {
-        if (callbackType != CALLBACK_LISTEN_ALL) return;
+    private void handleSuspended(Network network) {
         if (!network.equals(mTetheringUpstreamNetwork)) return;
         mLog.log("SUSPENDED current upstream: " + network);
     }
 
-    private void handleResumed(int callbackType, Network network) {
-        if (callbackType != CALLBACK_LISTEN_ALL) return;
+    private void handleResumed(Network network) {
         if (!network.equals(mTetheringUpstreamNetwork)) return;
         mLog.log("RESUMED current upstream: " + network);
     }
 
-    private void handleLost(int callbackType, Network network) {
-        if (network.equals(mDefaultInternetNetwork)) {
-            mDefaultInternetNetwork = null;
-            // There are few TODOs within ConnectivityService's rematching code
-            // pertaining to spurious onLost() notifications.
-            //
-            // TODO: simplify this, probably if favor of code that:
-            //     - selects a new upstream if mTetheringUpstreamNetwork has
-            //       been lost (by any callback)
-            //     - deletes the entry from the map only when the LISTEN_ALL
-            //       callback gets  notified.
-            if (callbackType == CALLBACK_DEFAULT_INTERNET) return;
-        }
+    private void handleLost(Network network) {
+        // There are few TODOs within ConnectivityService's rematching code
+        // pertaining to spurious onLost() notifications.
+        //
+        // TODO: simplify this, probably if favor of code that:
+        //     - selects a new upstream if mTetheringUpstreamNetwork has
+        //       been lost (by any callback)
+        //     - deletes the entry from the map only when the LISTEN_ALL
+        //       callback gets notified.
 
         if (!mNetworkMap.containsKey(network)) {
             // Ignore loss of networks about which we had not previously
@@ -393,11 +386,17 @@
 
         @Override
         public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
-            handleNetCap(mCallbackType, network, newNc);
+            if (mCallbackType == CALLBACK_DEFAULT_INTERNET) {
+                mDefaultInternetNetwork = network;
+                return;
+            }
+            handleNetCap(network, newNc);
         }
 
         @Override
         public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
+            if (mCallbackType == CALLBACK_DEFAULT_INTERNET) return;
+
             handleLinkProp(network, newLp);
             // Any non-LISTEN_ALL callback will necessarily concern a network that will
             // also match the LISTEN_ALL callback by construction of the LISTEN_ALL callback.
@@ -409,17 +408,25 @@
 
         @Override
         public void onNetworkSuspended(Network network) {
-            handleSuspended(mCallbackType, network);
+            if (mCallbackType == CALLBACK_LISTEN_ALL) {
+                handleSuspended(network);
+            }
         }
 
         @Override
         public void onNetworkResumed(Network network) {
-            handleResumed(mCallbackType, network);
+            if (mCallbackType == CALLBACK_LISTEN_ALL) {
+                handleResumed(network);
+            }
         }
 
         @Override
         public void onLost(Network network) {
-            handleLost(mCallbackType, network);
+            if (mCallbackType == CALLBACK_DEFAULT_INTERNET) {
+                mDefaultInternetNetwork = null;
+                return;
+            }
+            handleLost(network);
             // Any non-LISTEN_ALL callback will necessarily concern a network that will
             // also match the LISTEN_ALL callback by construction of the LISTEN_ALL callback.
             // So it's not useful to do this work for non-LISTEN_ALL callbacks.
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 78e18e9..1325f04 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -861,6 +861,11 @@
                         Slog.d(TAG, "Removing jobs for uid: " + uidRemoved);
                     }
                     cancelJobsForPackageAndUid(pkgName, uidRemoved, "app uninstalled");
+                    synchronized (mLock) {
+                        for (int c = 0; c < mControllers.size(); ++c) {
+                            mControllers.get(c).onAppRemovedLocked(pkgName, pkgUid);
+                        }
+                    }
                 }
             } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
                 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
@@ -868,6 +873,11 @@
                     Slog.d(TAG, "Removing jobs for user: " + userId);
                 }
                 cancelJobsForUser(userId);
+                synchronized (mLock) {
+                    for (int c = 0; c < mControllers.size(); ++c) {
+                        mControllers.get(c).onUserRemovedLocked(userId);
+                    }
+                }
             } else if (Intent.ACTION_QUERY_PACKAGE_RESTART.equals(action)) {
                 // Has this package scheduled any jobs, such that we will take action
                 // if it were to be force-stopped?
diff --git a/services/core/java/com/android/server/job/controllers/ConnectivityController.java b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
index 8f104e4..aca02bf 100644
--- a/services/core/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/services/core/java/com/android/server/job/controllers/ConnectivityController.java
@@ -296,6 +296,12 @@
         mRequestedWhitelistJobs.remove(uid);
     }
 
+    @GuardedBy("mLock")
+    @Override
+    public void onAppRemovedLocked(String pkgName, int uid) {
+        mTrackedJobs.delete(uid);
+    }
+
     /**
      * Test to see if running the given job on the given network is insane.
      * <p>
diff --git a/services/core/java/com/android/server/job/controllers/QuotaController.java b/services/core/java/com/android/server/job/controllers/QuotaController.java
index 660c238..58ee217 100644
--- a/services/core/java/com/android/server/job/controllers/QuotaController.java
+++ b/services/core/java/com/android/server/job/controllers/QuotaController.java
@@ -98,6 +98,19 @@
             data.put(packageName, obj);
         }
 
+        /** Removes all the data for the user, if there was any. */
+        public void delete(int userId) {
+            mData.delete(userId);
+        }
+
+        /** Removes the data for the user and package, if there was any. */
+        public void delete(int userId, @NonNull String packageName) {
+            ArrayMap<String, T> data = mData.get(userId);
+            if (data != null) {
+                data.remove(packageName);
+            }
+        }
+
         @Nullable
         public T get(int userId, @NonNull String packageName) {
             ArrayMap<String, T> data = mData.get(userId);
@@ -371,6 +384,38 @@
         }
     }
 
+    @Override
+    public void onAppRemovedLocked(String packageName, int uid) {
+        if (packageName == null) {
+            Slog.wtf(TAG, "Told app removed but given null package name.");
+            return;
+        }
+        final int userId = UserHandle.getUserId(uid);
+        mTrackedJobs.delete(userId, packageName);
+        Timer timer = mPkgTimers.get(userId, packageName);
+        if (timer != null) {
+            if (timer.isActive()) {
+                Slog.wtf(TAG, "onAppRemovedLocked called before Timer turned off.");
+                timer.dropEverything();
+            }
+            mPkgTimers.delete(userId, packageName);
+        }
+        mTimingSessions.delete(userId, packageName);
+        QcAlarmListener alarmListener = mInQuotaAlarmListeners.get(userId, packageName);
+        if (alarmListener != null) {
+            mAlarmManager.cancel(alarmListener);
+            mInQuotaAlarmListeners.delete(userId, packageName);
+        }
+    }
+
+    @Override
+    public void onUserRemovedLocked(int userId) {
+        mTrackedJobs.delete(userId);
+        mPkgTimers.delete(userId);
+        mTimingSessions.delete(userId);
+        mInQuotaAlarmListeners.delete(userId);
+    }
+
     /**
      * Returns an appropriate standby bucket for the job, taking into account any standby
      * exemptions.
@@ -882,6 +927,15 @@
             }
         }
 
+        /**
+         * Stops tracking all jobs and cancels any pending alarms. This should only be called if
+         * the Timer is not going to be used anymore.
+         */
+        void dropEverything() {
+            mRunningBgJobs.clear();
+            cancelCutoff();
+        }
+
         private void emitSessionLocked(long nowElapsed) {
             if (mBgJobCount <= 0) {
                 // Nothing to emit.
diff --git a/services/core/java/com/android/server/job/controllers/StateController.java b/services/core/java/com/android/server/job/controllers/StateController.java
index 61dc479..97c3bac 100644
--- a/services/core/java/com/android/server/job/controllers/StateController.java
+++ b/services/core/java/com/android/server/job/controllers/StateController.java
@@ -83,6 +83,28 @@
     public void onConstantsUpdatedLocked() {
     }
 
+    /** Called when a package is uninstalled from the device (not for an update). */
+    public void onAppRemovedLocked(String packageName, int uid) {
+    }
+
+    /** Called when a user is removed from the device. */
+    public void onUserRemovedLocked(int userId) {
+    }
+
+    /**
+     * Called when JobSchedulerService has determined that the job is not ready to be run. The
+     * Controller can evaluate if it can or should do something to promote this job's readiness.
+     */
+    public void evaluateStateLocked(JobStatus jobStatus) {
+    }
+
+    /**
+     * Called when something with the UID has changed. The controller should re-evaluate any
+     * internal state tracking dependent on this UID.
+     */
+    public void reevaluateStateLocked(int uid) {
+    }
+
     protected boolean wouldBeReadyWithConstraintLocked(JobStatus jobStatus, int constraint) {
         // This is very cheap to check (just a few conditions on data in JobStatus).
         final boolean jobWouldBeReady = jobStatus.wouldBeReadyWithConstraint(constraint);
@@ -100,20 +122,6 @@
         return mService.areComponentsInPlaceLocked(jobStatus);
     }
 
-    /**
-     * Called when JobSchedulerService has determined that the job is not ready to be run. The
-     * Controller can evaluate if it can or should do something to promote this job's readiness.
-     */
-    public void evaluateStateLocked(JobStatus jobStatus) {
-    }
-
-    /**
-     * Called when something with the UID has changed. The controller should re-evaluate any
-     * internal state tracking dependent on this UID.
-     */
-    public void reevaluateStateLocked(int uid) {
-    }
-
     public abstract void dumpControllerStateLocked(IndentingPrintWriter pw,
             Predicate<JobStatus> predicate);
     public abstract void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index fc364c0..36a027a 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -583,7 +583,6 @@
     private void loadPolicyFile() {
         if (DBG) Slog.d(TAG, "loadPolicyFile");
         synchronized (mPolicyFile) {
-
             InputStream infile = null;
             try {
                 infile = mPolicyFile.openRead();
@@ -3357,14 +3356,12 @@
                 Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user);
                 return null;
             }
-            synchronized(mPolicyFile) {
-                final ByteArrayOutputStream baos = new ByteArrayOutputStream();
-                try {
-                    writePolicyXml(baos, true /*forBackup*/);
-                    return baos.toByteArray();
-                } catch (IOException e) {
-                    Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
-                }
+            final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            try {
+                writePolicyXml(baos, true /*forBackup*/);
+                return baos.toByteArray();
+            } catch (IOException e) {
+                Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e);
             }
             return null;
         }
@@ -3383,14 +3380,12 @@
                 Slog.w(TAG, "applyRestore: cannot restore policy for user " + user);
                 return;
             }
-            synchronized(mPolicyFile) {
-                final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
-                try {
-                    readPolicyXml(bais, true /*forRestore*/);
-                    handleSavePolicyFile();
-                } catch (NumberFormatException | XmlPullParserException | IOException e) {
-                    Slog.w(TAG, "applyRestore: error reading payload", e);
-                }
+            final ByteArrayInputStream bais = new ByteArrayInputStream(payload);
+            try {
+                readPolicyXml(bais, true /*forRestore*/);
+                handleSavePolicyFile();
+            } catch (NumberFormatException | XmlPullParserException | IOException e) {
+                Slog.w(TAG, "applyRestore: error reading payload", e);
             }
         }
 
diff --git a/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
new file mode 100644
index 0000000..2ae424d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DynamicCodeLoggingService.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2018 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.pm;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.android.server.pm.dex.DexLogger;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Scheduled job to trigger logging of app dynamic code loading. This runs daily while idle and
+ * charging. The actual logging is performed by {@link DexLogger}.
+ * {@hide}
+ */
+public class DynamicCodeLoggingService extends JobService {
+    private static final String TAG = DynamicCodeLoggingService.class.getName();
+
+    private static final int JOB_ID = 2030028;
+    private static final long PERIOD_MILLIS = TimeUnit.DAYS.toMillis(1);
+
+    private volatile boolean mStopRequested = false;
+
+    private static final boolean DEBUG = false;
+
+    /**
+     * Schedule our job with the {@link JobScheduler}.
+     */
+    public static void schedule(Context context) {
+        ComponentName serviceName = new ComponentName(
+                "android", DynamicCodeLoggingService.class.getName());
+
+        JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+        js.schedule(new JobInfo.Builder(JOB_ID, serviceName)
+                .setRequiresDeviceIdle(true)
+                .setRequiresCharging(true)
+                .setPeriodic(PERIOD_MILLIS)
+                .build());
+        if (DEBUG) {
+            Log.d(TAG, "Job scheduled");
+        }
+    }
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        if (DEBUG) {
+            Log.d(TAG, "onStartJob");
+        }
+        mStopRequested = false;
+        new IdleLoggingThread(params).start();
+        return true;  // Job is running on another thread
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        if (DEBUG) {
+            Log.d(TAG, "onStopJob");
+        }
+        mStopRequested = true;
+        return true;  // Requests job be re-scheduled.
+    }
+
+    private class IdleLoggingThread extends Thread {
+        private final JobParameters mParams;
+
+        IdleLoggingThread(JobParameters params) {
+            super("DynamicCodeLoggingService_IdleLoggingJob");
+            mParams = params;
+        }
+
+        @Override
+        public void run() {
+            if (DEBUG) {
+                Log.d(TAG, "Starting logging run");
+            }
+
+            PackageManagerService pm = (PackageManagerService) ServiceManager.getService("package");
+            DexLogger dexLogger = pm.getDexManager().getDexLogger();
+            for (String packageName : dexLogger.getAllPackagesWithDynamicCodeLoading()) {
+                if (mStopRequested) {
+                    Log.w(TAG, "Stopping logging run at scheduler request");
+                    return;
+                }
+
+                dexLogger.logDynamicCodeLoading(packageName);
+            }
+
+            jobFinished(mParams, /* reschedule */ false);
+            if (DEBUG) {
+                Log.d(TAG, "Finished logging run");
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3e9100e..6cfb846 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -24,7 +24,6 @@
 import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
 import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
-import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
 import static android.content.Intent.ACTION_MAIN;
 import static android.content.Intent.CATEGORY_DEFAULT;
 import static android.content.Intent.CATEGORY_HOME;
@@ -304,7 +303,6 @@
 import com.android.server.pm.Settings.DatabaseVersion;
 import com.android.server.pm.Settings.VersionInfo;
 import com.android.server.pm.dex.ArtManagerService;
-import com.android.server.pm.dex.DexLogger;
 import com.android.server.pm.dex.DexManager;
 import com.android.server.pm.dex.DexoptOptions;
 import com.android.server.pm.dex.PackageDexUsage;
@@ -2168,10 +2166,7 @@
 
         mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
                 "*dexopt*");
-        DexManager.Listener dexManagerListener = DexLogger.getListener(this,
-                installer, mInstallLock);
-        mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock,
-                dexManagerListener);
+        mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock);
         mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);
         mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
 
@@ -9215,7 +9210,7 @@
 
     /**
      * Reconcile the information we have about the secondary dex files belonging to
-     * {@code packagName} and the actual dex files. For all dex files that were
+     * {@code packageName} and the actual dex files. For all dex files that were
      * deleted, update the internal records and delete the generated oat files.
      */
     @Override
@@ -20185,11 +20180,6 @@
                 if (Process.isIsolated(uid)) {
                     return Zygote.MOUNT_EXTERNAL_NONE;
                 }
-                if (StorageManager.hasIsolatedStorage()) {
-                    return checkUidPermission(WRITE_MEDIA_STORAGE, uid) == PERMISSION_GRANTED
-                            ? Zygote.MOUNT_EXTERNAL_FULL
-                            : Zygote.MOUNT_EXTERNAL_WRITE;
-                }
                 if (checkUidPermission(READ_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) {
                     return Zygote.MOUNT_EXTERNAL_DEFAULT;
                 }
diff --git a/services/core/java/com/android/server/pm/dex/DexLogger.java b/services/core/java/com/android/server/pm/dex/DexLogger.java
index 88d9e52..68a755b 100644
--- a/services/core/java/com/android/server/pm/dex/DexLogger.java
+++ b/services/core/java/com/android/server/pm/dex/DexLogger.java
@@ -18,29 +18,32 @@
 
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.os.FileUtils;
 import android.os.RemoteException;
-
-import android.util.ArraySet;
+import android.os.storage.StorageManager;
 import android.util.ByteStringUtils;
 import android.util.EventLog;
 import android.util.PackageUtils;
 import android.util.Slog;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.pm.Installer;
 import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.dex.PackageDynamicCodeLoading.DynamicCodeFile;
+import com.android.server.pm.dex.PackageDynamicCodeLoading.PackageDynamicCode;
 
 import java.io.File;
+import java.util.Map;
 import java.util.Set;
 
-import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
-
 /**
  * This class is responsible for logging data about secondary dex files.
  * The data logged includes hashes of the name and content of each file.
  */
-public class DexLogger implements DexManager.Listener {
+public class DexLogger {
     private static final String TAG = "DexLogger";
 
     // Event log tag & subtag used for SafetyNet logging of dynamic
@@ -49,75 +52,172 @@
     private static final String DCL_SUBTAG = "dcl";
 
     private final IPackageManager mPackageManager;
+    private final PackageDynamicCodeLoading mPackageDynamicCodeLoading;
     private final Object mInstallLock;
     @GuardedBy("mInstallLock")
     private final Installer mInstaller;
 
-    public static DexManager.Listener getListener(IPackageManager pms,
-            Installer installer, Object installLock) {
-        return new DexLogger(pms, installer, installLock);
+    public DexLogger(IPackageManager pms, Installer installer, Object installLock) {
+        this(pms, installer, installLock, new PackageDynamicCodeLoading());
     }
 
     @VisibleForTesting
-    /*package*/ DexLogger(IPackageManager pms, Installer installer, Object installLock) {
+    DexLogger(IPackageManager pms, Installer installer, Object installLock,
+            PackageDynamicCodeLoading packageDynamicCodeLoading) {
         mPackageManager = pms;
+        mPackageDynamicCodeLoading = packageDynamicCodeLoading;
         mInstaller = installer;
         mInstallLock = installLock;
     }
 
-    /**
-     * Compute and log hashes of the name and content of a secondary dex file.
-     */
-    @Override
-    public void onReconcileSecondaryDexFile(ApplicationInfo appInfo, DexUseInfo dexUseInfo,
-            String dexPath, int storageFlags) {
-        int ownerUid = appInfo.uid;
+    public Set<String> getAllPackagesWithDynamicCodeLoading() {
+        return mPackageDynamicCodeLoading.getAllPackagesWithDynamicCodeLoading();
+    }
 
-        byte[] hash = null;
-        synchronized(mInstallLock) {
-            try {
-                hash = mInstaller.hashSecondaryDexFile(dexPath, appInfo.packageName,
-                        ownerUid, appInfo.volumeUuid, storageFlags);
-            } catch (InstallerException e) {
-                Slog.e(TAG, "Got InstallerException when hashing dex " + dexPath +
-                        " : " + e.getMessage());
-            }
-        }
-        if (hash == null) {
+    /**
+     * Write information about code dynamically loaded by {@code packageName} to the event log.
+     */
+    public void logDynamicCodeLoading(String packageName) {
+        PackageDynamicCode info = getPackageDynamicCodeInfo(packageName);
+        if (info == null) {
             return;
         }
 
-        String dexFileName = new File(dexPath).getName();
-        String message = PackageUtils.computeSha256Digest(dexFileName.getBytes());
-        // Valid SHA256 will be 256 bits, 32 bytes.
-        if (hash.length == 32) {
-            message = message + ' ' + ByteStringUtils.toHexString(hash);
-        }
+        SparseArray<ApplicationInfo> appInfoByUser = new SparseArray<>();
+        boolean needWrite = false;
 
-        writeDclEvent(ownerUid, message);
+        for (Map.Entry<String, DynamicCodeFile> fileEntry : info.mFileUsageMap.entrySet()) {
+            String filePath = fileEntry.getKey();
+            DynamicCodeFile fileInfo = fileEntry.getValue();
+            int userId = fileInfo.mUserId;
 
-        if (dexUseInfo.isUsedByOtherApps()) {
-            Set<String> otherPackages = dexUseInfo.getLoadingPackages();
-            Set<Integer> otherUids = new ArraySet<>(otherPackages.size());
-            for (String otherPackageName : otherPackages) {
+            int index = appInfoByUser.indexOfKey(userId);
+            ApplicationInfo appInfo;
+            if (index >= 0) {
+                appInfo = appInfoByUser.get(userId);
+            } else {
+                appInfo = null;
+
                 try {
-                    int otherUid = mPackageManager.getPackageUid(
-                        otherPackageName, /*flags*/0, dexUseInfo.getOwnerUserId());
-                    if (otherUid != -1 && otherUid != ownerUid) {
-                        otherUids.add(otherUid);
-                    }
-                } catch (RemoteException ignore) {
+                    PackageInfo ownerInfo =
+                            mPackageManager.getPackageInfo(packageName, /*flags*/ 0, userId);
+                    appInfo = ownerInfo == null ? null : ownerInfo.applicationInfo;
+                } catch (RemoteException ignored) {
                     // Can't happen, we're local.
                 }
+                appInfoByUser.put(userId, appInfo);
+                if (appInfo == null) {
+                    Slog.d(TAG, "Could not find package " + packageName + " for user " + userId);
+                    // Package has probably been uninstalled for user.
+                    needWrite |= mPackageDynamicCodeLoading.removeUserPackage(packageName, userId);
+                }
             }
-            for (int otherUid : otherUids) {
-                writeDclEvent(otherUid, message);
+
+            if (appInfo == null) {
+                continue;
             }
+
+            int storageFlags;
+            if (appInfo.deviceProtectedDataDir != null
+                    && FileUtils.contains(appInfo.deviceProtectedDataDir, filePath)) {
+                storageFlags = StorageManager.FLAG_STORAGE_DE;
+            } else if (appInfo.credentialProtectedDataDir != null
+                    && FileUtils.contains(appInfo.credentialProtectedDataDir, filePath)) {
+                storageFlags = StorageManager.FLAG_STORAGE_CE;
+            } else {
+                Slog.e(TAG, "Could not infer CE/DE storage for path " + filePath);
+                needWrite |= mPackageDynamicCodeLoading.removeFile(packageName, filePath, userId);
+                continue;
+            }
+
+            byte[] hash = null;
+            synchronized (mInstallLock) {
+                try {
+                    hash = mInstaller.hashSecondaryDexFile(filePath, packageName, appInfo.uid,
+                            appInfo.volumeUuid, storageFlags);
+                } catch (InstallerException e) {
+                    Slog.e(TAG, "Got InstallerException when hashing file " + filePath
+                            + ": " + e.getMessage());
+                }
+            }
+
+            String fileName = new File(filePath).getName();
+            String message = PackageUtils.computeSha256Digest(fileName.getBytes());
+
+            // Valid SHA256 will be 256 bits, 32 bytes.
+            if (hash != null && hash.length == 32) {
+                message = message + ' ' + ByteStringUtils.toHexString(hash);
+            } else {
+                Slog.d(TAG, "Got no hash for " + filePath);
+                // File has probably been deleted.
+                needWrite |= mPackageDynamicCodeLoading.removeFile(packageName, filePath, userId);
+            }
+
+            for (String loadingPackageName : fileInfo.mLoadingPackages) {
+                int loadingUid = -1;
+                if (loadingPackageName.equals(packageName)) {
+                    loadingUid = appInfo.uid;
+                } else {
+                    try {
+                        loadingUid = mPackageManager.getPackageUid(loadingPackageName, /*flags*/ 0,
+                                userId);
+                    } catch (RemoteException ignored) {
+                        // Can't happen, we're local.
+                    }
+                }
+
+                if (loadingUid != -1) {
+                    writeDclEvent(loadingUid, message);
+                }
+            }
+        }
+
+        if (needWrite) {
+            mPackageDynamicCodeLoading.maybeWriteAsync();
         }
     }
 
     @VisibleForTesting
-    /*package*/ void writeDclEvent(int uid, String message) {
+    PackageDynamicCode getPackageDynamicCodeInfo(String packageName) {
+        return mPackageDynamicCodeLoading.getPackageDynamicCodeInfo(packageName);
+    }
+
+    @VisibleForTesting
+    void writeDclEvent(int uid, String message) {
         EventLog.writeEvent(SNET_TAG, DCL_SUBTAG, uid, message);
     }
+
+    void record(int loaderUserId, String dexPath,
+            String owningPackageName, String loadingPackageName) {
+        if (mPackageDynamicCodeLoading.record(owningPackageName, dexPath,
+                PackageDynamicCodeLoading.FILE_TYPE_DEX, loaderUserId,
+                loadingPackageName)) {
+            mPackageDynamicCodeLoading.maybeWriteAsync();
+        }
+    }
+
+    void clear() {
+        mPackageDynamicCodeLoading.clear();
+    }
+
+    void removePackage(String packageName) {
+        if (mPackageDynamicCodeLoading.removePackage(packageName)) {
+            mPackageDynamicCodeLoading.maybeWriteAsync();
+        }
+    }
+
+    void removeUserPackage(String packageName, int userId) {
+        if (mPackageDynamicCodeLoading.removeUserPackage(packageName, userId)) {
+            mPackageDynamicCodeLoading.maybeWriteAsync();
+        }
+    }
+
+    void readAndSync(Map<String, Set<Integer>> packageToUsersMap) {
+        mPackageDynamicCodeLoading.read();
+        mPackageDynamicCodeLoading.syncData(packageToUsersMap);
+    }
+
+    void writeNow() {
+        mPackageDynamicCodeLoading.writeNow();
+    }
 }
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 36b7269..25ef767 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -19,7 +19,6 @@
 import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
 import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
 import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
-import static com.android.server.pm.dex.PackageDynamicCodeLoading.PackageDynamicCode;
 
 import android.content.ContentResolver;
 import android.content.Context;
@@ -90,18 +89,17 @@
     // encode and save the dex usage data.
     private final PackageDexUsage mPackageDexUsage;
 
-    // PackageDynamicCodeLoading handles recording of dynamic code loading -
-    // which is similar to PackageDexUsage but records a different aspect of the data.
+    // DexLogger handles recording of dynamic code loading - which is similar to PackageDexUsage
+    // but records a different aspect of the data.
     // (It additionally includes DEX files loaded with unsupported class loaders, and doesn't
     // record class loaders or ISAs.)
-    private final PackageDynamicCodeLoading mPackageDynamicCodeLoading;
+    private final DexLogger mDexLogger;
 
     private final IPackageManager mPackageManager;
     private final PackageDexOptimizer mPackageDexOptimizer;
     private final Object mInstallLock;
     @GuardedBy("mInstallLock")
     private final Installer mInstaller;
-    private final Listener mListener;
 
     // Possible outcomes of a dex search.
     private static int DEX_SEARCH_NOT_FOUND = 0;  // dex file not found
@@ -122,26 +120,20 @@
      */
     private final static PackageUseInfo DEFAULT_USE_INFO = new PackageUseInfo();
 
-    public interface Listener {
-        /**
-         * Invoked just before the secondary dex file {@code dexPath} for the specified application
-         * is reconciled.
-         */
-        void onReconcileSecondaryDexFile(ApplicationInfo appInfo, DexUseInfo dexUseInfo,
-                String dexPath, int storageFlags);
-    }
-
     public DexManager(Context context, IPackageManager pms, PackageDexOptimizer pdo,
-            Installer installer, Object installLock, Listener listener) {
+            Installer installer, Object installLock) {
         mContext = context;
         mPackageCodeLocationsCache = new HashMap<>();
         mPackageDexUsage = new PackageDexUsage();
-        mPackageDynamicCodeLoading = new PackageDynamicCodeLoading();
         mPackageManager = pms;
         mPackageDexOptimizer = pdo;
         mInstaller = installer;
         mInstallLock = installLock;
-        mListener = listener;
+        mDexLogger = new DexLogger(pms, installer, installLock);
+    }
+
+    public DexLogger getDexLogger() {
+        return mDexLogger;
     }
 
     public void systemReady() {
@@ -243,11 +235,8 @@
                     continue;
                 }
 
-                if (mPackageDynamicCodeLoading.record(searchResult.mOwningPackageName, dexPath,
-                        PackageDynamicCodeLoading.FILE_TYPE_DEX, loaderUserId,
-                        loadingAppInfo.packageName)) {
-                    mPackageDynamicCodeLoading.maybeWriteAsync();
-                }
+                mDexLogger.record(loaderUserId, dexPath, searchResult.mOwningPackageName,
+                        loadingAppInfo.packageName);
 
                 if (classLoaderContexts != null) {
 
@@ -284,7 +273,7 @@
             loadInternal(existingPackages);
         } catch (Exception e) {
             mPackageDexUsage.clear();
-            mPackageDynamicCodeLoading.clear();
+            mDexLogger.clear();
             Slog.w(TAG, "Exception while loading. Starting with a fresh state.", e);
         }
     }
@@ -335,16 +324,12 @@
             if (mPackageDexUsage.removePackage(packageName)) {
                 mPackageDexUsage.maybeWriteAsync();
             }
-            if (mPackageDynamicCodeLoading.removePackage(packageName)) {
-                mPackageDynamicCodeLoading.maybeWriteAsync();
-            }
+            mDexLogger.removePackage(packageName);
         } else {
             if (mPackageDexUsage.removeUserPackage(packageName, userId)) {
                 mPackageDexUsage.maybeWriteAsync();
             }
-            if (mPackageDynamicCodeLoading.removeUserPackage(packageName, userId)) {
-                mPackageDynamicCodeLoading.maybeWriteAsync();
-            }
+            mDexLogger.removeUserPackage(packageName, userId);
         }
     }
 
@@ -423,10 +408,9 @@
         }
 
         try {
-            mPackageDynamicCodeLoading.read();
-            mPackageDynamicCodeLoading.syncData(packageToUsersMap);
+            mDexLogger.readAndSync(packageToUsersMap);
         } catch (Exception e) {
-            mPackageDynamicCodeLoading.clear();
+            mDexLogger.clear();
             Slog.w(TAG, "Exception while loading package dynamic code usage. "
                     + "Starting with a fresh state.", e);
         }
@@ -460,11 +444,6 @@
         return mPackageDexUsage.getPackageUseInfo(packageName) != null;
     }
 
-    @VisibleForTesting
-    /*package*/ PackageDynamicCode getPackageDynamicCodeInfo(String packageName) {
-        return mPackageDynamicCodeLoading.getPackageDynamicCodeInfo(packageName);
-    }
-
     /**
      * Perform dexopt on with the given {@code options} on the secondary dex files.
      * @return true if all secondary dex files were processed successfully (compiled or skipped
@@ -574,10 +553,6 @@
                 continue;
             }
 
-            if (mListener != null) {
-                mListener.onReconcileSecondaryDexFile(info, dexUseInfo, dexPath, flags);
-            }
-
             boolean dexStillExists = true;
             synchronized(mInstallLock) {
                 try {
@@ -721,7 +696,7 @@
      */
     public void writePackageDexUsageNow() {
         mPackageDexUsage.writeNow();
-        mPackageDynamicCodeLoading.writeNow();
+        mDexLogger.writeNow();
     }
 
     private void registerSettingObserver() {
diff --git a/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java b/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java
index f74aa1d..6d4bc82 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDynamicCodeLoading.java
@@ -62,6 +62,13 @@
     private static final String PACKAGE_SEPARATOR = ",";
 
     /**
+     * Limit on how many files we store for a single owner, to avoid one app causing
+     * unbounded memory consumption.
+     */
+    @VisibleForTesting
+    static final int MAX_FILES_PER_OWNER = 100;
+
+    /**
      * Regular expression to match the expected format of an input line describing one file.
      * <p>Example: {@code D:10:package.name1,package.name2:/escaped/path}
      * <p>The capturing groups are the file type, user ID, loading packages and escaped file path
@@ -515,6 +522,9 @@
         private boolean add(String path, char fileType, int userId, String loadingPackage) {
             DynamicCodeFile fileInfo = mFileUsageMap.get(path);
             if (fileInfo == null) {
+                if (mFileUsageMap.size() >= MAX_FILES_PER_OWNER) {
+                    return false;
+                }
                 fileInfo = new DynamicCodeFile(fileType, userId, loadingPackage);
                 mFileUsageMap.put(path, fileInfo);
                 return true;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 31f5ce4..b58c8116 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -799,10 +799,6 @@
                     continue;
                 }
 
-                if (bp.isRemoved()) {
-                    continue;
-                }
-
                 // Limit ephemeral apps to ephemeral allowed permissions.
                 if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
                     if (DEBUG_PERMISSIONS) {
@@ -951,7 +947,8 @@
                                     // how to disable the API to simulate revocation as legacy
                                     // apps don't expect to run with revoked permissions.
                                     if (PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName())) {
-                                        if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
+                                        if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0
+                                                && !bp.isRemoved()) {
                                             flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
                                             // We changed the flags, hence have to write.
                                             updatedUserIds = ArrayUtils.appendInt(
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 3291a45..ced5935 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -19,12 +19,16 @@
 import android.Manifest;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
+import android.app.AlarmManager;
+import android.app.AlarmManager.OnAlarmListener;
 import android.app.admin.DevicePolicyManager;
 import android.hardware.biometrics.BiometricSourceType;
 import android.app.trust.ITrustListener;
 import android.app.trust.ITrustManager;
+import android.app.UserSwitchObserver;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -35,7 +39,9 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.database.ContentObserver;
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
 import android.os.DeadObjectException;
@@ -50,6 +56,7 @@
 import android.provider.Settings;
 import android.service.trust.TrustAgentService;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.Log;
@@ -58,11 +65,13 @@
 import android.util.Xml;
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.SystemService;
+
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -106,8 +115,11 @@
     private static final int MSG_STOP_USER = 12;
     private static final int MSG_DISPATCH_UNLOCK_LOCKOUT = 13;
     private static final int MSG_REFRESH_DEVICE_LOCKED_FOR_USER = 14;
+    private static final int MSG_SCHEDULE_TRUST_TIMEOUT = 15;
 
     private static final int TRUST_USUALLY_MANAGED_FLUSH_DELAY = 2 * 60 * 1000;
+    private static final String TRUST_TIMEOUT_ALARM_TAG = "TrustManagerService.trustTimeoutForUser";
+    private static final long TRUST_TIMEOUT_IN_MILLIS = 20 * 1000; //4 * 60 * 60 * 1000;
 
     private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<>();
     private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<>();
@@ -132,6 +144,11 @@
     @GuardedBy("mUsersUnlockedByBiometric")
     private final SparseBooleanArray mUsersUnlockedByBiometric = new SparseBooleanArray();
 
+    private final ArrayMap<Integer, TrustTimeoutAlarmListener> mTrustTimeoutAlarmListenerForUser =
+            new ArrayMap<>();
+    private AlarmManager mAlarmManager;
+    private final SettingsObserver mSettingsObserver;
+
     private final StrongAuthTracker mStrongAuthTracker;
 
     private boolean mTrustAgentsCanRun = false;
@@ -144,6 +161,8 @@
         mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
         mLockPatternUtils = new LockPatternUtils(context);
         mStrongAuthTracker = new StrongAuthTracker(context);
+        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+        mSettingsObserver = new SettingsObserver(mHandler);
     }
 
     @Override
@@ -170,7 +189,130 @@
         }
     }
 
-    // Agent management
+    // Extend unlock config and logic
+
+    private final class SettingsObserver extends ContentObserver {
+        private final Uri TRUST_AGENTS_EXTEND_UNLOCK =
+                Settings.Secure.getUriFor(Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK);
+
+        private final Uri LOCK_SCREEN_WHEN_TRUST_LOST =
+                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST);
+
+        private final ContentResolver mContentResolver;
+        private boolean mTrustAgentsExtendUnlock;
+        private boolean mLockWhenTrustLost;
+
+        /**
+         * Creates a settings observer
+         *
+         * @param handler The handler to run {@link #onChange} on, or null if none.
+         */
+        SettingsObserver(Handler handler) {
+            super(handler);
+            mContentResolver = getContext().getContentResolver();
+            updateContentObserver();
+        }
+
+        void updateContentObserver() {
+            mContentResolver.unregisterContentObserver(this);
+            mContentResolver.registerContentObserver(TRUST_AGENTS_EXTEND_UNLOCK,
+                    false /* notifyForDescendents */,
+                    this /* observer */,
+                    mCurrentUser);
+            mContentResolver.registerContentObserver(LOCK_SCREEN_WHEN_TRUST_LOST,
+                    false /* notifyForDescendents */,
+                    this /* observer */,
+                    mCurrentUser);
+
+            // Update the value immediately
+            onChange(true /* selfChange */, TRUST_AGENTS_EXTEND_UNLOCK);
+            onChange(true /* selfChange */, LOCK_SCREEN_WHEN_TRUST_LOST);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (TRUST_AGENTS_EXTEND_UNLOCK.equals(uri)) {
+                mTrustAgentsExtendUnlock =
+                        Settings.Secure.getIntForUser(
+                                mContentResolver,
+                                Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK,
+                                0 /* default */,
+                                mCurrentUser) != 0;
+            } else if (LOCK_SCREEN_WHEN_TRUST_LOST.equals(uri)) {
+                mLockWhenTrustLost =
+                        Settings.Secure.getIntForUser(
+                                mContentResolver,
+                                Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST,
+                                0 /* default */,
+                                mCurrentUser) != 0;
+            }
+        }
+
+        boolean getTrustAgentsExtendUnlock() {
+            return mTrustAgentsExtendUnlock;
+        }
+
+        boolean getLockWhenTrustLost() {
+            return mLockWhenTrustLost;
+        }
+    }
+
+    private void maybeLockScreen(int userId) {
+        if (userId != mCurrentUser) {
+            return;
+        }
+
+        if (mSettingsObserver.getLockWhenTrustLost()) {
+            if (DEBUG) Slog.d(TAG, "Locking device because trust was lost");
+            try {
+                WindowManagerGlobal.getWindowManagerService().lockNow(null);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Error locking screen when trust was lost");
+            }
+
+            // If active unlocking is not allowed, cancel any pending trust timeouts because the
+            // screen is already locked.
+            TrustTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId);
+            if (alarm != null && mSettingsObserver.getTrustAgentsExtendUnlock()) {
+                mAlarmManager.cancel(alarm);
+                alarm.setQueued(false /* isQueued */);
+            }
+        }
+    }
+
+    private void scheduleTrustTimeout(int userId, boolean override) {
+        int shouldOverride = override ? 1 : 0;
+        if (override) {
+            shouldOverride = 1;
+        }
+        mHandler.obtainMessage(MSG_SCHEDULE_TRUST_TIMEOUT, userId, shouldOverride).sendToTarget();
+    }
+
+    private void handleScheduleTrustTimeout(int userId, int shouldOverride) {
+        long when = SystemClock.elapsedRealtime() + TRUST_TIMEOUT_IN_MILLIS;
+        userId = mCurrentUser;
+        TrustTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId);
+
+        // Cancel existing trust timeouts for this user if needed.
+        if (alarm != null) {
+            if (shouldOverride == 0 && alarm.isQueued()) {
+                if (DEBUG) Slog.d(TAG, "Found existing trust timeout alarm. Skipping.");
+                return;
+            }
+            mAlarmManager.cancel(alarm);
+        } else {
+            alarm = new TrustTimeoutAlarmListener(userId);
+            mTrustTimeoutAlarmListenerForUser.put(userId, alarm);
+        }
+
+        if (DEBUG) Slog.d(TAG, "\tSetting up trust timeout alarm");
+        alarm.setQueued(true /* isQueued */);
+        mAlarmManager.setExact(
+                AlarmManager.ELAPSED_REALTIME_WAKEUP, when, TRUST_TIMEOUT_ALARM_TAG, alarm,
+                mHandler);
+    }
+
+   // Agent management
 
     private static final class AgentInfo {
         CharSequence label;
@@ -202,14 +344,36 @@
         }
     }
 
+
     public void updateTrust(int userId, int flags) {
+        updateTrust(userId, flags, false /* isFromUnlock */);
+    }
+
+    private void updateTrust(int userId, int flags, boolean isFromUnlock) {
         boolean managed = aggregateIsTrustManaged(userId);
         dispatchOnTrustManagedChanged(managed, userId);
         if (mStrongAuthTracker.isTrustAllowedForUser(userId)
                 && isTrustUsuallyManagedInternal(userId) != managed) {
             updateTrustUsuallyManaged(userId, managed);
         }
+
         boolean trusted = aggregateIsTrusted(userId);
+        IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+        boolean showingKeyguard = true;
+        try {
+            showingKeyguard = wm.isKeyguardLocked();
+        } catch (RemoteException e) {
+        }
+
+        if (mSettingsObserver.getTrustAgentsExtendUnlock()) {
+            trusted = trusted && (!showingKeyguard || isFromUnlock) && userId == mCurrentUser;
+            if (DEBUG) {
+                Slog.d(TAG, "Extend unlock setting trusted as " + Boolean.toString(trusted)
+                        + " && " + Boolean.toString(!showingKeyguard)
+                        + " && " + Boolean.toString(userId == mCurrentUser));
+            }
+        }
+
         boolean changed;
         synchronized (mUserIsTrusted) {
             changed = mUserIsTrusted.get(userId) != trusted;
@@ -218,6 +382,11 @@
         dispatchOnTrustChanged(trusted, userId, flags);
         if (changed) {
             refreshDeviceLockedForUser(userId);
+            if (!trusted) {
+                maybeLockScreen(userId);
+            } else {
+                scheduleTrustTimeout(userId, false /* override */);
+            }
         }
     }
 
@@ -704,6 +873,8 @@
     private void dispatchUnlockAttempt(boolean successful, int userId) {
         if (successful) {
             mStrongAuthTracker.allowTrustFromUnlock(userId);
+            // Allow the presence of trust on a successful unlock attempt to extend unlock.
+            updateTrust(userId, 0 /* flags */, true);
         }
 
         for (int i = 0; i < mActiveAgents.size(); i++) {
@@ -1033,8 +1204,11 @@
             synchronized(mUsersUnlockedByBiometric) {
                 mUsersUnlockedByBiometric.put(userId, true);
             }
+            // In extend unlock mode we need to refresh trust state here, which will call
+            // refreshDeviceLockedForUser()
+            int updateTrustOnUnlock = mSettingsObserver.getTrustAgentsExtendUnlock() ? 1 : 0;
             mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, userId,
-                    0 /* arg2 */).sendToTarget();
+                    updateTrustOnUnlock).sendToTarget();
         }
 
         @Override
@@ -1114,6 +1288,7 @@
                     break;
                 case MSG_SWITCH_USER:
                     mCurrentUser = msg.arg1;
+                    mSettingsObserver.updateContentObserver();
                     refreshDeviceLockedForUser(UserHandle.USER_ALL);
                     break;
                 case MSG_STOP_USER:
@@ -1134,8 +1309,14 @@
                     }
                     break;
                 case MSG_REFRESH_DEVICE_LOCKED_FOR_USER:
+                    if (msg.arg2 == 1) {
+                        updateTrust(msg.arg1, 0 /* flags */, true);
+                    }
                     refreshDeviceLockedForUser(msg.arg1);
                     break;
+                case MSG_SCHEDULE_TRUST_TIMEOUT:
+                    handleScheduleTrustTimeout(msg.arg1, msg.arg2);
+                    break;
             }
         }
     };
@@ -1245,6 +1426,15 @@
                         + " agentsCanRun=" + canAgentsRunForUser(userId));
             }
 
+            // Cancel pending alarms if we require some auth anyway.
+            if (!isTrustAllowedForUser(userId)) {
+                TrustTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId);
+                if (alarm != null && alarm.isQueued()) {
+                    alarm.setQueued(false /* isQueued */);
+                    mAlarmManager.cancel(alarm);
+                }
+            }
+
             refreshAgentList(userId);
 
             // The list of active trust agents may not have changed, if there was a previous call
@@ -1283,4 +1473,35 @@
             }
         }
     }
+
+    private class TrustTimeoutAlarmListener implements OnAlarmListener {
+        private final int mUserId;
+        private boolean mIsQueued = false;
+
+        TrustTimeoutAlarmListener(int userId) {
+            mUserId = userId;
+        }
+
+        @Override
+        public void onAlarm() {
+            mIsQueued = false;
+            int strongAuthState = mStrongAuthTracker.getStrongAuthForUser(mUserId);
+
+            // Only fire if trust can unlock.
+            if (mStrongAuthTracker.isTrustAllowedForUser(mUserId)) {
+                if (DEBUG) Slog.d(TAG, "Revoking all trust because of trust timeout");
+                mLockPatternUtils.requireStrongAuth(
+                        mStrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, mUserId);
+            }
+            maybeLockScreen(mUserId);
+        }
+
+        public void setQueued(boolean isQueued) {
+            mIsQueued = isQueued;
+        }
+
+        public boolean isQueued() {
+            return mIsQueued;
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 12690a9..10231826 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -3,6 +3,9 @@
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.ActivityManager.processStateAmToProto;
+import static android.app.WaitResult.LAUNCH_STATE_COLD;
+import static android.app.WaitResult.LAUNCH_STATE_HOT;
+import static android.app.WaitResult.LAUNCH_STATE_WARM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
@@ -80,6 +83,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT;
 
+import android.app.WaitResult;
 import android.app.WindowConfiguration.WindowingMode;
 import android.content.Context;
 import android.content.Intent;
@@ -101,10 +105,10 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.SomeArgs;
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 
 /**
@@ -259,6 +263,19 @@
             activityRecordIdHashCode = System.identityHashCode(launchedActivity);
             this.windowsFullyDrawnDelayMs = windowsFullyDrawnDelayMs;
         }
+
+        @WaitResult.LaunchState int getLaunchState() {
+            switch (type) {
+                case TYPE_TRANSITION_WARM_LAUNCH:
+                    return LAUNCH_STATE_WARM;
+                case TYPE_TRANSITION_HOT_LAUNCH:
+                    return LAUNCH_STATE_HOT;
+                case TYPE_TRANSITION_COLD_LAUNCH:
+                    return LAUNCH_STATE_COLD;
+                default:
+                    return -1;
+            }
+        }
     }
 
     ActivityMetricsLogger(ActivityStackSupervisor supervisor, Context context, Looper looper) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 608e450..4373675 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -145,6 +145,7 @@
 import android.app.PendingIntent;
 import android.app.PictureInPictureParams;
 import android.app.ResultInfo;
+import android.app.WaitResult.LaunchState;
 import android.app.servertransaction.ActivityConfigurationChangeItem;
 import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ActivityRelaunchItem;
@@ -2016,10 +2017,7 @@
         stopped = false;
 
         if (isActivityTypeHome()) {
-            WindowProcessController app = task.mActivities.get(0).app;
-            if (hasProcess() && app != mAtmService.mHomeProcess) {
-                mAtmService.mHomeProcess = app;
-            }
+            mStackSupervisor.updateHomeProcess(task.mActivities.get(0).app);
         }
 
         if (nowVisible) {
@@ -2183,7 +2181,7 @@
                 .getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle);
         if (info != null) {
             mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
-                    info.windowsFullyDrawnDelayMs);
+                    info.windowsFullyDrawnDelayMs, info.getLaunchState());
         }
     }
 
@@ -2207,8 +2205,9 @@
             final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
                     .getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestamp);
             final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
+            final @LaunchState int launchState = info != null ? info.getLaunchState() : -1;
             mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
-                    windowsDrawnDelayMs);
+                    windowsDrawnDelayMs, launchState);
             mStackSupervisor.sendWaitingVisibleReportLocked(this);
             finishLaunchTickingLocked();
             if (task != null) {
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 4339e513..f58b83d 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -179,6 +179,7 @@
     static final int LAUNCH_TASK_BEHIND_COMPLETE = FIRST_SUPERVISOR_STACK_MSG + 12;
     static final int REPORT_MULTI_WINDOW_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 14;
     static final int REPORT_PIP_MODE_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 15;
+    static final int REPORT_HOME_CHANGED_MSG = FIRST_SUPERVISOR_STACK_MSG + 16;
 
     // Used to indicate that windows of activities should be preserved during the resize.
     static final boolean PRESERVE_WINDOWS = true;
@@ -598,7 +599,8 @@
         }
     }
 
-    void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r, long totalTime) {
+    void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r, long totalTime,
+            @WaitResult.LaunchState int launchState) {
         boolean changed = false;
         for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) {
             WaitResult w = mWaitingActivityLaunched.remove(i);
@@ -609,6 +611,7 @@
                     w.who = new ComponentName(r.info.packageName, r.info.name);
                 }
                 w.totalTime = totalTime;
+                w.launchState = launchState;
                 // Do not modify w.result.
             }
         }
@@ -793,7 +796,7 @@
                         System.identityHashCode(r), task.taskId, r.shortComponentName);
                 if (r.isActivityTypeHome()) {
                     // Home process is the root process of the task.
-                    mService.mHomeProcess = task.mActivities.get(0).app;
+                    updateHomeProcess(task.mActivities.get(0).app);
                 }
                 mService.getPackageManagerInternalLocked().notifyPackageUse(
                         r.intent.getComponent().getPackageName(), NOTIFY_PACKAGE_USE_ACTIVITY);
@@ -915,6 +918,15 @@
         return true;
     }
 
+    void updateHomeProcess(WindowProcessController app) {
+        if (app != null && mService.mHomeProcess != app) {
+            if (!mHandler.hasMessages(REPORT_HOME_CHANGED_MSG)) {
+                mHandler.sendEmptyMessage(REPORT_HOME_CHANGED_MSG);
+            }
+            mService.mHomeProcess = app;
+        }
+    }
+
     private void logIfTransactionTooLarge(Intent intent, Bundle icicle) {
         int extrasSize = 0;
         if (intent != null) {
@@ -1242,7 +1254,8 @@
             mHandler.removeMessages(IDLE_TIMEOUT_MSG, r);
             r.finishLaunchTickingLocked();
             if (fromTimeout) {
-                reportActivityLaunchedLocked(fromTimeout, r, INVALID_DELAY);
+                reportActivityLaunchedLocked(fromTimeout, r, INVALID_DELAY,
+                        -1 /* launchState */);
             }
 
             // This is a hack to semi-deal with a race condition
@@ -2540,7 +2553,15 @@
                         }
                     }
                 } break;
+                case REPORT_HOME_CHANGED_MSG: {
+                    synchronized (mService.mGlobalLock) {
+                        mHandler.removeMessages(REPORT_HOME_CHANGED_MSG);
 
+                        // Start home activities on displays with no activities.
+                        mRootActivityContainer.startHomeOnEmptyDisplays("homeChanged");
+                    }
+                }
+                break;
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index c5b42f9..84a32fc 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -336,6 +336,15 @@
         return homeStarted;
     }
 
+    void startHomeOnEmptyDisplays(String reason) {
+        for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
+            final ActivityDisplay display = mActivityDisplays.get(i);
+            if (display.topRunningActivity() == null) {
+                startHomeOnDisplay(mCurrentUser, reason, display.mDisplayId);
+            }
+        }
+    }
+
     /**
      * This starts home activity on displays that can have system decorations and only if the
      * home activity can have multiple instances.
diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
index 649f1a5..4d4a7b4 100644
--- a/services/core/jni/com_android_server_net_NetworkStatsService.cpp
+++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp
@@ -34,7 +34,6 @@
 #include "netdbpf/BpfNetworkStats.h"
 
 using android::bpf::Stats;
-using android::bpf::hasBpfSupport;
 using android::bpf::bpfGetUidStats;
 using android::bpf::bpfGetIfaceStats;
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 88f645d..e1b83fc 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -109,6 +109,7 @@
 import com.android.server.os.SchedulingPolicyService;
 import com.android.server.pm.BackgroundDexOptService;
 import com.android.server.pm.CrossProfileAppsService;
+import com.android.server.pm.DynamicCodeLoggingService;
 import com.android.server.pm.Installer;
 import com.android.server.pm.LauncherAppsService;
 import com.android.server.pm.OtaDexoptService;
@@ -1667,6 +1668,18 @@
             traceEnd();
 
             if (!isWatch) {
+                // We don't run this on watches as there are no plans to use the data logged
+                // on watch devices.
+                traceBeginAndSlog("StartDynamicCodeLoggingService");
+                try {
+                    DynamicCodeLoggingService.schedule(context);
+                } catch (Throwable e) {
+                    reportWtf("starting DynamicCodeLoggingService", e);
+                }
+                traceEnd();
+            }
+
+            if (!isWatch) {
                 traceBeginAndSlog("StartPruneInstantAppsJobService");
                 try {
                     PruneInstantAppsJobService.schedule(context);
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index a7209a0..f037905 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -111,7 +111,7 @@
      * the last writable 32bit word.
      */
     @VisibleForTesting
-    private static enum Counter {
+    public static enum Counter {
         RESERVED_OOB,  // Points to offset 0 from the end of the buffer (out-of-bounds)
         TOTAL_PACKETS,
         PASSED_ARP,
@@ -139,7 +139,8 @@
         DROPPED_IPV6_MULTICAST_PING,
         DROPPED_IPV6_NON_ICMP_MULTICAST,
         DROPPED_802_3_FRAME,
-        DROPPED_ETHERTYPE_BLACKLISTED;
+        DROPPED_ETHERTYPE_BLACKLISTED,
+        DROPPED_ARP_REPLY_SPA_NO_HOST;
 
         // Returns the negative byte offset from the end of the APF data segment for
         // a given counter.
@@ -156,7 +157,7 @@
     /**
      * When APFv4 is supported, loads R1 with the offset of the specified counter.
      */
-    private void maybeSetCounter(ApfGenerator gen, Counter c) {
+    private void maybeSetupCounter(ApfGenerator gen, Counter c) {
         if (mApfCapabilities.hasDataAccess()) {
             gen.addLoadImmediate(Register.R1, c.offset());
         }
@@ -288,16 +289,18 @@
     private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28;
 
     private static final int ARP_HEADER_OFFSET = ETH_HEADER_LEN;
-    private static final int ARP_OPCODE_OFFSET = ARP_HEADER_OFFSET + 6;
-    private static final short ARP_OPCODE_REQUEST = 1;
-    private static final short ARP_OPCODE_REPLY = 2;
     private static final byte[] ARP_IPV4_HEADER = {
             0, 1, // Hardware type: Ethernet (1)
             8, 0, // Protocol type: IP (0x0800)
             6,    // Hardware size: 6
             4,    // Protocol size: 4
     };
-    private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
+    private static final int ARP_OPCODE_OFFSET = ARP_HEADER_OFFSET + 6;
+    // Opcode: ARP request (0x0001), ARP reply (0x0002)
+    private static final short ARP_OPCODE_REQUEST = 1;
+    private static final short ARP_OPCODE_REPLY = 2;
+    private static final int ARP_SOURCE_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 14;
+    private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 24;
     // Do not log ApfProgramEvents whose actual lifetimes was less than this.
     private static final int APF_PROGRAM_EVENT_LIFETIME_THRESHOLD = 2;
     // Limit on the Black List size to cap on program usage for this
@@ -816,7 +819,7 @@
                     gen.addJumpIfR0LessThan(filterLifetime, nextFilterLabel);
                 }
             }
-            maybeSetCounter(gen, Counter.DROPPED_RA);
+            maybeSetupCounter(gen, Counter.DROPPED_RA);
             gen.addJump(mCountAndDropLabel);
             gen.defineLabel(nextFilterLabel);
             return filterLifetime;
@@ -883,6 +886,8 @@
         //   pass
         // if not ARP IPv4 reply or request
         //   pass
+        // if ARP reply source ip is 0.0.0.0
+        //   drop
         // if unicast ARP reply
         //   pass
         // if interface has no IPv4 address
@@ -897,18 +902,23 @@
 
         // Pass if not ARP IPv4.
         gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET);
-        maybeSetCounter(gen, Counter.PASSED_ARP_NON_IPV4);
+        maybeSetupCounter(gen, Counter.PASSED_ARP_NON_IPV4);
         gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, mCountAndPassLabel);
 
         // Pass if unknown ARP opcode.
         gen.addLoad16(Register.R0, ARP_OPCODE_OFFSET);
         gen.addJumpIfR0Equals(ARP_OPCODE_REQUEST, checkTargetIPv4); // Skip to unicast check
-        maybeSetCounter(gen, Counter.PASSED_ARP_UNKNOWN);
+        maybeSetupCounter(gen, Counter.PASSED_ARP_UNKNOWN);
         gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, mCountAndPassLabel);
 
+        // Drop if ARP reply source IP is 0.0.0.0
+        gen.addLoad32(Register.R0, ARP_SOURCE_IP_ADDRESS_OFFSET);
+        maybeSetupCounter(gen, Counter.DROPPED_ARP_REPLY_SPA_NO_HOST);
+        gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel);
+
         // Pass if unicast reply.
         gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
-        maybeSetCounter(gen, Counter.PASSED_ARP_UNICAST_REPLY);
+        maybeSetupCounter(gen, Counter.PASSED_ARP_UNICAST_REPLY);
         gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
 
         // Either a unicast request, a unicast reply, or a broadcast reply.
@@ -916,17 +926,17 @@
         if (mIPv4Address == null) {
             // When there is no IPv4 address, drop GARP replies (b/29404209).
             gen.addLoad32(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
-            maybeSetCounter(gen, Counter.DROPPED_GARP_REPLY);
+            maybeSetupCounter(gen, Counter.DROPPED_GARP_REPLY);
             gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel);
         } else {
             // When there is an IPv4 address, drop unicast/broadcast requests
             // and broadcast replies with a different target IPv4 address.
             gen.addLoadImmediate(Register.R0, ARP_TARGET_IP_ADDRESS_OFFSET);
-            maybeSetCounter(gen, Counter.DROPPED_ARP_OTHER_HOST);
+            maybeSetupCounter(gen, Counter.DROPPED_ARP_OTHER_HOST);
             gen.addJumpIfBytesNotEqual(Register.R0, mIPv4Address, mCountAndDropLabel);
         }
 
-        maybeSetCounter(gen, Counter.PASSED_ARP);
+        maybeSetupCounter(gen, Counter.PASSED_ARP);
         gen.addJump(mCountAndPassLabel);
     }
 
@@ -970,7 +980,7 @@
             // NOTE: Relies on R1 containing IPv4 header offset.
             gen.addAddR1();
             gen.addJumpIfBytesNotEqual(Register.R0, mHardwareAddress, skipDhcpv4Filter);
-            maybeSetCounter(gen, Counter.PASSED_DHCP);
+            maybeSetupCounter(gen, Counter.PASSED_DHCP);
             gen.addJump(mCountAndPassLabel);
 
             // Drop all multicasts/broadcasts.
@@ -979,30 +989,30 @@
             // If IPv4 destination address is in multicast range, drop.
             gen.addLoad8(Register.R0, IPV4_DEST_ADDR_OFFSET);
             gen.addAnd(0xf0);
-            maybeSetCounter(gen, Counter.DROPPED_IPV4_MULTICAST);
+            maybeSetupCounter(gen, Counter.DROPPED_IPV4_MULTICAST);
             gen.addJumpIfR0Equals(0xe0, mCountAndDropLabel);
 
             // If IPv4 broadcast packet, drop regardless of L2 (b/30231088).
-            maybeSetCounter(gen, Counter.DROPPED_IPV4_BROADCAST_ADDR);
+            maybeSetupCounter(gen, Counter.DROPPED_IPV4_BROADCAST_ADDR);
             gen.addLoad32(Register.R0, IPV4_DEST_ADDR_OFFSET);
             gen.addJumpIfR0Equals(IPV4_BROADCAST_ADDRESS, mCountAndDropLabel);
             if (mIPv4Address != null && mIPv4PrefixLength < 31) {
-                maybeSetCounter(gen, Counter.DROPPED_IPV4_BROADCAST_NET);
+                maybeSetupCounter(gen, Counter.DROPPED_IPV4_BROADCAST_NET);
                 int broadcastAddr = ipv4BroadcastAddress(mIPv4Address, mIPv4PrefixLength);
                 gen.addJumpIfR0Equals(broadcastAddr, mCountAndDropLabel);
             }
 
             // If L2 broadcast packet, drop.
             // TODO: can we invert this condition to fall through to the common pass case below?
-            maybeSetCounter(gen, Counter.PASSED_IPV4_UNICAST);
+            maybeSetupCounter(gen, Counter.PASSED_IPV4_UNICAST);
             gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
             gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
-            maybeSetCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST);
+            maybeSetupCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST);
             gen.addJump(mCountAndDropLabel);
         }
 
         // Otherwise, pass
-        maybeSetCounter(gen, Counter.PASSED_IPV4);
+        maybeSetupCounter(gen, Counter.PASSED_IPV4);
         gen.addJump(mCountAndPassLabel);
     }
 
@@ -1050,16 +1060,16 @@
 
             // Drop all other packets sent to ff00::/8 (multicast prefix).
             gen.defineLabel(dropAllIPv6MulticastsLabel);
-            maybeSetCounter(gen, Counter.DROPPED_IPV6_NON_ICMP_MULTICAST);
+            maybeSetupCounter(gen, Counter.DROPPED_IPV6_NON_ICMP_MULTICAST);
             gen.addLoad8(Register.R0, IPV6_DEST_ADDR_OFFSET);
             gen.addJumpIfR0Equals(0xff, mCountAndDropLabel);
             // Not multicast. Pass.
-            maybeSetCounter(gen, Counter.PASSED_IPV6_UNICAST_NON_ICMP);
+            maybeSetupCounter(gen, Counter.PASSED_IPV6_UNICAST_NON_ICMP);
             gen.addJump(mCountAndPassLabel);
             gen.defineLabel(skipIPv6MulticastFilterLabel);
         } else {
             // If not ICMPv6, pass.
-            maybeSetCounter(gen, Counter.PASSED_IPV6_NON_ICMP);
+            maybeSetupCounter(gen, Counter.PASSED_IPV6_NON_ICMP);
             gen.addJumpIfR0NotEquals(IPPROTO_ICMPV6, mCountAndPassLabel);
         }
 
@@ -1069,7 +1079,7 @@
         String skipUnsolicitedMulticastNALabel = "skipUnsolicitedMulticastNA";
         gen.addLoad8(Register.R0, ICMP6_TYPE_OFFSET);
         // Drop all router solicitations (b/32833400)
-        maybeSetCounter(gen, Counter.DROPPED_IPV6_ROUTER_SOLICITATION);
+        maybeSetupCounter(gen, Counter.DROPPED_IPV6_ROUTER_SOLICITATION);
         gen.addJumpIfR0Equals(ICMPV6_ROUTER_SOLICITATION, mCountAndDropLabel);
         // If not neighbor announcements, skip filter.
         gen.addJumpIfR0NotEquals(ICMPV6_NEIGHBOR_ADVERTISEMENT, skipUnsolicitedMulticastNALabel);
@@ -1078,7 +1088,7 @@
         gen.addLoadImmediate(Register.R0, IPV6_DEST_ADDR_OFFSET);
         gen.addJumpIfBytesNotEqual(Register.R0, IPV6_ALL_NODES_ADDRESS,
                 skipUnsolicitedMulticastNALabel);
-        maybeSetCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA);
+        maybeSetupCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA);
         gen.addJump(mCountAndDropLabel);
         gen.defineLabel(skipUnsolicitedMulticastNALabel);
     }
@@ -1108,7 +1118,7 @@
 
         if (mApfCapabilities.hasDataAccess()) {
             // Increment TOTAL_PACKETS
-            maybeSetCounter(gen, Counter.TOTAL_PACKETS);
+            maybeSetupCounter(gen, Counter.TOTAL_PACKETS);
             gen.addLoadData(Register.R0, 0);  // load counter
             gen.addAdd(1);
             gen.addStoreData(Register.R0, 0);  // write-back counter
@@ -1134,12 +1144,12 @@
 
         if (mDrop802_3Frames) {
             // drop 802.3 frames (ethtype < 0x0600)
-            maybeSetCounter(gen, Counter.DROPPED_802_3_FRAME);
+            maybeSetupCounter(gen, Counter.DROPPED_802_3_FRAME);
             gen.addJumpIfR0LessThan(ETH_TYPE_MIN, mCountAndDropLabel);
         }
 
         // Handle ether-type black list
-        maybeSetCounter(gen, Counter.DROPPED_ETHERTYPE_BLACKLISTED);
+        maybeSetupCounter(gen, Counter.DROPPED_ETHERTYPE_BLACKLISTED);
         for (int p : mEthTypeBlackList) {
             gen.addJumpIfR0Equals(p, mCountAndDropLabel);
         }
@@ -1168,9 +1178,9 @@
 
         // Drop non-IP non-ARP broadcasts, pass the rest
         gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET);
-        maybeSetCounter(gen, Counter.PASSED_NON_IP_UNICAST);
+        maybeSetupCounter(gen, Counter.PASSED_NON_IP_UNICAST);
         gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
-        maybeSetCounter(gen, Counter.DROPPED_ETH_BROADCAST);
+        maybeSetupCounter(gen, Counter.DROPPED_ETH_BROADCAST);
         gen.addJump(mCountAndDropLabel);
 
         // Add IPv6 filters:
@@ -1193,7 +1203,7 @@
 
         // Execution will reach the bottom of the program if none of the filters match,
         // which will pass the packet to the application processor.
-        maybeSetCounter(gen, Counter.PASSED_IPV6_ICMP);
+        maybeSetupCounter(gen, Counter.PASSED_IPV6_ICMP);
 
         // Append the count & pass trampoline, which increments the counter at the data address
         // pointed to by R1, then jumps to the pass label. This saves a few bytes over inserting
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 71aec23..effb5a7 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -270,6 +270,60 @@
     }
 
     @Test
+    public void testOnAppRemovedLocked() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.saveTimingSession(0, "com.android.test.remove",
+                createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+        mQuotaController.saveTimingSession(0, "com.android.test.remove",
+                createTimingSession(
+                        now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5));
+        mQuotaController.saveTimingSession(0, "com.android.test.remove",
+                createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1));
+        // Test that another app isn't affected.
+        TimingSession one = createTimingSession(
+                now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3);
+        TimingSession two = createTimingSession(
+                now - (70 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1);
+        List<TimingSession> expected = new ArrayList<>();
+        // Added in correct (chronological) order.
+        expected.add(two);
+        expected.add(one);
+        mQuotaController.saveTimingSession(0, "com.android.test.stay", two);
+        mQuotaController.saveTimingSession(0, "com.android.test.stay", one);
+
+        mQuotaController.onAppRemovedLocked("com.android.test.remove", 10001);
+        assertNull(mQuotaController.getTimingSessions(0, "com.android.test.remove"));
+        assertEquals(expected, mQuotaController.getTimingSessions(0, "com.android.test.stay"));
+    }
+
+    @Test
+    public void testOnUserRemovedLocked() {
+        final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (6 * HOUR_IN_MILLIS), 10 * MINUTE_IN_MILLIS, 5));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(
+                        now - (2 * HOUR_IN_MILLIS + MINUTE_IN_MILLIS), 6 * MINUTE_IN_MILLIS, 5));
+        mQuotaController.saveTimingSession(0, "com.android.test",
+                createTimingSession(now - (HOUR_IN_MILLIS), MINUTE_IN_MILLIS, 1));
+        // Test that another user isn't affected.
+        TimingSession one = createTimingSession(
+                now - 10 * MINUTE_IN_MILLIS, 9 * MINUTE_IN_MILLIS, 3);
+        TimingSession two = createTimingSession(
+                now - (70 * MINUTE_IN_MILLIS), 9 * MINUTE_IN_MILLIS, 1);
+        List<TimingSession> expected = new ArrayList<>();
+        // Added in correct (chronological) order.
+        expected.add(two);
+        expected.add(one);
+        mQuotaController.saveTimingSession(10, "com.android.test", two);
+        mQuotaController.saveTimingSession(10, "com.android.test", one);
+
+        mQuotaController.onUserRemovedLocked(0);
+        assertNull(mQuotaController.getTimingSessions(0, "com.android.test"));
+        assertEquals(expected, mQuotaController.getTimingSessions(10, "com.android.test"));
+    }
+
+    @Test
     public void testGetTrailingExecutionTimeLocked_NoTimer() {
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
         // Added in chronological order.
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index d7b1cb4..4ee9551 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -70,7 +70,7 @@
         "liblzma",
         "libnativehelper",
         "libui",
-        "libunwind",
+        "libunwindstack",
         "libutils",
         "netd_aidl_interface-cpp",
     ],
diff --git a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
index e6b328a..ec5d93e 100644
--- a/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/StorageManagerServiceTest.java
@@ -29,8 +29,7 @@
 import android.os.UserManagerInternal;
 import android.os.storage.StorageManagerInternal;
 
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.os.Zygote;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -38,6 +37,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class StorageManagerServiceTest {
@@ -97,15 +99,15 @@
         when(mPm.getPackagesForUid(eq(UID_GREY))).thenReturn(new String[] { PKG_GREY });
         when(mPm.getPackagesForUid(eq(UID_COLORS))).thenReturn(new String[] { PKG_RED, PKG_BLUE });
 
-        setIsAppStorageSandboxed(PID_BLUE, UID_COLORS, true);
-        setIsAppStorageSandboxed(PID_GREY, UID_GREY, true);
-        setIsAppStorageSandboxed(PID_RED, UID_COLORS, true);
+        setStorageMountMode(PID_BLUE, UID_COLORS, Zygote.MOUNT_EXTERNAL_WRITE);
+        setStorageMountMode(PID_GREY, UID_GREY, Zygote.MOUNT_EXTERNAL_WRITE);
+        setStorageMountMode(PID_RED, UID_COLORS, Zygote.MOUNT_EXTERNAL_WRITE);
 
         mService = new StorageManagerService(mContext);
     }
 
-    private void setIsAppStorageSandboxed(int pid, int uid, boolean sandboxed) {
-        when(mAmi.isAppStorageSandboxed(pid, uid)).thenReturn(sandboxed);
+    private void setStorageMountMode(int pid, int uid, int mountMode) {
+        when(mAmi.getStorageMountMode(pid, uid)).thenReturn(mountMode);
     }
 
     @Test
@@ -210,7 +212,7 @@
 
     @Test
     public void testPackageNotSandboxed() throws Exception {
-        setIsAppStorageSandboxed(PID_RED, UID_COLORS, false);
+        setStorageMountMode(PID_RED, UID_COLORS, Zygote.MOUNT_EXTERNAL_FULL);
 
         // Both app and system have the same view
         assertTranslation(
@@ -224,6 +226,29 @@
                 PID_RED, UID_COLORS);
     }
 
+    @Test
+    public void testInstallerPackage() throws Exception {
+        setStorageMountMode(PID_GREY, UID_GREY, Zygote.MOUNT_EXTERNAL_INSTALLER);
+
+        assertTranslation(
+                "/storage/emulated/0/Android/obb/com.grey/foo.jpg",
+                "/storage/emulated/0/Android/obb/com.grey/foo.jpg",
+                PID_GREY, UID_GREY);
+        assertTranslation(
+                "/storage/emulated/0/Android/obb/com.blue/bar.jpg",
+                "/storage/emulated/0/Android/obb/com.blue/bar.jpg",
+                PID_GREY, UID_GREY);
+
+        assertTranslation(
+                "/storage/emulated/0/Android/data/com.grey/foo.jpg",
+                "/storage/emulated/0/Android/data/com.grey/foo.jpg",
+                PID_GREY, UID_GREY);
+        assertTranslation(
+                "/storage/emulated/0/Android/sandbox/com.grey/Android/data/com.blue/bar.jpg",
+                "/storage/emulated/0/Android/data/com.blue/bar.jpg",
+                PID_GREY, UID_GREY);
+    }
+
     private void assertTranslation(String system, String sandbox,
             int pid, int uid) throws Exception {
         assertEquals(system,
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
index 87c3cd2..3b6b48b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexLoggerTests.java
@@ -16,14 +16,20 @@
 
 package com.android.server.pm.dex;
 
-import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.FILE_TYPE_DEX;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.atMost;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
 import android.os.storage.StorageManager;
 
 import androidx.test.filters.SmallTest;
@@ -43,13 +49,12 @@
 import org.mockito.junit.MockitoJUnit;
 import org.mockito.junit.MockitoRule;
 import org.mockito.quality.Strictness;
-
-import java.util.Arrays;
+import org.mockito.stubbing.Stubber;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class DexLoggerTests {
-    private static final String PACKAGE_NAME = "package.name";
+    private static final String OWNING_PACKAGE_NAME = "package.name";
     private static final String VOLUME_UUID = "volUuid";
     private static final String DEX_PATH = "/bar/foo.jar";
     private static final int STORAGE_FLAGS = StorageManager.FLAG_STORAGE_DE;
@@ -66,6 +71,7 @@
     };
     private static final String CONTENT_HASH =
             "0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20";
+    private static final byte[] EMPTY_BYTES = {};
 
     @Rule public MockitoRule mockito = MockitoJUnit.rule().strictness(Strictness.STRICT_STUBS);
 
@@ -73,92 +79,191 @@
     @Mock Installer mInstaller;
     private final Object mInstallLock = new Object();
 
-    private DexManager.Listener mListener;
+    private PackageDynamicCodeLoading mPackageDynamicCodeLoading;
+    private DexLogger mDexLogger;
 
     private final ListMultimap<Integer, String> mMessagesForUid = ArrayListMultimap.create();
+    private boolean mWriteTriggered = false;
+    private static final String EXPECTED_MESSAGE_WITH_CONTENT_HASH =
+            DEX_FILENAME_HASH + " " + CONTENT_HASH;
 
     @Before
-    public void setup() {
+    public void setup() throws Exception {
+        // Disable actually attempting to do file writes.
+        mPackageDynamicCodeLoading = new PackageDynamicCodeLoading() {
+            @Override
+            void maybeWriteAsync() {
+                mWriteTriggered = true;
+            }
+
+            @Override
+            protected void writeNow(Void data) {
+                throw new AssertionError("These tests should never call this method.");
+            }
+        };
+
         // For test purposes capture log messages as well as sending to the event log.
-        mListener = new DexLogger(mPM, mInstaller, mInstallLock) {
-                @Override
+        mDexLogger = new DexLogger(mPM, mInstaller, mInstallLock, mPackageDynamicCodeLoading) {
+            @Override
                 void writeDclEvent(int uid, String message) {
                     super.writeDclEvent(uid, message);
                     mMessagesForUid.put(uid, message);
                 }
             };
+
+        // Make the owning package exist in our mock PackageManager.
+        ApplicationInfo appInfo = new ApplicationInfo();
+        appInfo.deviceProtectedDataDir = "/bar";
+        appInfo.uid = OWNER_UID;
+        appInfo.volumeUuid = VOLUME_UUID;
+        PackageInfo packageInfo = new PackageInfo();
+        packageInfo.applicationInfo = appInfo;
+
+        doReturn(packageInfo).when(mPM)
+                .getPackageInfo(OWNING_PACKAGE_NAME, /*flags*/ 0, OWNER_USER_ID);
     }
 
     @Test
-    public void testSingleAppWithFileHash() throws Exception {
-        doReturn(CONTENT_HASH_BYTES).when(mInstaller).hashSecondaryDexFile(
-            DEX_PATH, PACKAGE_NAME, OWNER_UID, VOLUME_UUID, STORAGE_FLAGS);
+    public void testOneLoader_ownFile_withFileHash() throws Exception {
+        whenFileIsHashed(DEX_PATH, doReturn(CONTENT_HASH_BYTES));
 
-        runOnReconcile();
+        recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
-        assertThat(mMessagesForUid.keySet()).containsExactly(OWNER_UID);
-        String expectedMessage = DEX_FILENAME_HASH + " " + CONTENT_HASH;
-        assertThat(mMessagesForUid).containsEntry(OWNER_UID, expectedMessage);
+        assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID);
+        assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
+
+        assertThat(mWriteTriggered).isFalse();
+        assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading())
+                .containsExactly(OWNING_PACKAGE_NAME);
     }
 
     @Test
-    public void testSingleAppNoFileHash() throws Exception {
-        doReturn(new byte[] { }).when(mInstaller).hashSecondaryDexFile(
-            DEX_PATH, PACKAGE_NAME, OWNER_UID, VOLUME_UUID, STORAGE_FLAGS);
+    public void testOneLoader_ownFile_noFileHash() throws Exception {
+        whenFileIsHashed(DEX_PATH, doReturn(EMPTY_BYTES));
 
-        runOnReconcile();
+        recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
-        assertThat(mMessagesForUid.keySet()).containsExactly(OWNER_UID);
+        assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID);
         assertThat(mMessagesForUid).containsEntry(OWNER_UID, DEX_FILENAME_HASH);
+
+        // File should be removed from the DCL list, since we can't hash it.
+        assertThat(mWriteTriggered).isTrue();
+        assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty();
     }
 
     @Test
-    public void testSingleAppHashFails() throws Exception {
-        doThrow(new InstallerException("Testing failure")).when(mInstaller).hashSecondaryDexFile(
-            DEX_PATH, PACKAGE_NAME, OWNER_UID, VOLUME_UUID, STORAGE_FLAGS);
+    public void testOneLoader_ownFile_hashingFails() throws Exception {
+        whenFileIsHashed(DEX_PATH, doThrow(new InstallerException("Intentional failure for test")));
 
-        runOnReconcile();
+        recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+
+        assertThat(mMessagesForUid.keys()).containsExactly(OWNER_UID);
+        assertThat(mMessagesForUid).containsEntry(OWNER_UID, DEX_FILENAME_HASH);
+
+        // File should be removed from the DCL list, since we can't hash it.
+        assertThat(mWriteTriggered).isTrue();
+        assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty();
+    }
+
+    @Test
+    public void testOneLoader_ownFile_unknownPath() {
+        recordLoad(OWNING_PACKAGE_NAME, "other/path");
+        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
         assertThat(mMessagesForUid).isEmpty();
+        assertThat(mWriteTriggered).isTrue();
+        assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty();
     }
 
     @Test
-    public void testOtherApps() throws Exception {
-        doReturn(CONTENT_HASH_BYTES).when(mInstaller).hashSecondaryDexFile(
-            DEX_PATH, PACKAGE_NAME, OWNER_UID, VOLUME_UUID, STORAGE_FLAGS);
+    public void testOneLoader_differentOwner() throws Exception {
+        whenFileIsHashed(DEX_PATH, doReturn(CONTENT_HASH_BYTES));
+        setPackageUid("other.package.name", 1001);
 
-        // Simulate three packages from two different UIDs
-        String packageName1 = "other1.package.name";
-        String packageName2 = "other2.package.name";
-        String packageName3 = "other3.package.name";
-        int uid1 = 1001;
-        int uid2 = 1002;
+        recordLoad("other.package.name", DEX_PATH);
+        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
-        doReturn(uid1).when(mPM).getPackageUid(packageName1, 0, OWNER_USER_ID);
-        doReturn(uid2).when(mPM).getPackageUid(packageName2, 0, OWNER_USER_ID);
-        doReturn(uid1).when(mPM).getPackageUid(packageName3, 0, OWNER_USER_ID);
-
-        runOnReconcile(packageName1, packageName2, packageName3);
-
-        assertThat(mMessagesForUid.keySet()).containsExactly(OWNER_UID, uid1, uid2);
-
-        String expectedMessage = DEX_FILENAME_HASH + " " + CONTENT_HASH;
-        assertThat(mMessagesForUid).containsEntry(OWNER_UID, expectedMessage);
-        assertThat(mMessagesForUid).containsEntry(uid1, expectedMessage);
-        assertThat(mMessagesForUid).containsEntry(uid2, expectedMessage);
+        assertThat(mMessagesForUid.keys()).containsExactly(1001);
+        assertThat(mMessagesForUid).containsEntry(1001, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
+        assertThat(mWriteTriggered).isFalse();
     }
 
-    private void runOnReconcile(String... otherPackageNames) {
-        ApplicationInfo appInfo = new ApplicationInfo();
-        appInfo.packageName = PACKAGE_NAME;
-        appInfo.volumeUuid = VOLUME_UUID;
-        appInfo.uid = OWNER_UID;
+    @Test
+    public void testOneLoader_differentOwner_uninstalled() throws Exception {
+        whenFileIsHashed(DEX_PATH, doReturn(CONTENT_HASH_BYTES));
+        setPackageUid("other.package.name", -1);
 
-        boolean isUsedByOtherApps = otherPackageNames.length > 0;
-        DexUseInfo dexUseInfo = new DexUseInfo(
-            isUsedByOtherApps, OWNER_USER_ID, /* classLoaderContext */ null, /* loaderIsa */ null);
-        dexUseInfo.getLoadingPackages().addAll(Arrays.asList(otherPackageNames));
+        recordLoad("other.package.name", DEX_PATH);
+        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
 
-        mListener.onReconcileSecondaryDexFile(appInfo, dexUseInfo, DEX_PATH, STORAGE_FLAGS);
+        assertThat(mMessagesForUid).isEmpty();
+        assertThat(mWriteTriggered).isFalse();
+    }
+
+    @Test
+    public void testMultipleLoadersAndFiles() throws Exception {
+        String otherDexPath = "/bar/nosuchdir/foo.jar";
+        whenFileIsHashed(DEX_PATH, doReturn(CONTENT_HASH_BYTES));
+        whenFileIsHashed(otherDexPath, doReturn(EMPTY_BYTES));
+        setPackageUid("other.package.name1", 1001);
+        setPackageUid("other.package.name2", 1002);
+
+        recordLoad("other.package.name1", DEX_PATH);
+        recordLoad("other.package.name1", otherDexPath);
+        recordLoad("other.package.name2", DEX_PATH);
+        recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+
+        assertThat(mMessagesForUid.keys()).containsExactly(1001, 1001, 1002, OWNER_UID);
+        assertThat(mMessagesForUid).containsEntry(1001, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
+        assertThat(mMessagesForUid).containsEntry(1001, DEX_FILENAME_HASH);
+        assertThat(mMessagesForUid).containsEntry(1002, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
+        assertThat(mMessagesForUid).containsEntry(OWNER_UID, EXPECTED_MESSAGE_WITH_CONTENT_HASH);
+
+        assertThat(mWriteTriggered).isTrue();
+        assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading())
+                .containsExactly(OWNING_PACKAGE_NAME);
+
+        // Check the DexLogger caching is working
+        verify(mPM, atMost(1)).getPackageInfo(OWNING_PACKAGE_NAME, /*flags*/ 0, OWNER_USER_ID);
+    }
+
+    @Test
+    public void testUnknownOwner() {
+        reset(mPM);
+        recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+        mDexLogger.logDynamicCodeLoading("other.package.name");
+
+        assertThat(mMessagesForUid).isEmpty();
+        assertThat(mWriteTriggered).isFalse();
+        verifyZeroInteractions(mPM);
+    }
+
+    @Test
+    public void testUninstalledPackage() {
+        reset(mPM);
+        recordLoad(OWNING_PACKAGE_NAME, DEX_PATH);
+        mDexLogger.logDynamicCodeLoading(OWNING_PACKAGE_NAME);
+
+        assertThat(mMessagesForUid).isEmpty();
+        assertThat(mWriteTriggered).isTrue();
+        assertThat(mDexLogger.getAllPackagesWithDynamicCodeLoading()).isEmpty();
+    }
+
+    private void setPackageUid(String packageName, int uid) throws Exception {
+        doReturn(uid).when(mPM).getPackageUid(packageName, /*flags*/ 0, OWNER_USER_ID);
+    }
+
+    private void whenFileIsHashed(String dexPath, Stubber stubber) throws Exception {
+        stubber.when(mInstaller).hashSecondaryDexFile(
+                dexPath, OWNING_PACKAGE_NAME, OWNER_UID, VOLUME_UUID, STORAGE_FLAGS);
+    }
+
+    private void recordLoad(String loadingPackageName, String dexPath) {
+        mPackageDynamicCodeLoading.record(
+                OWNING_PACKAGE_NAME, dexPath, FILE_TYPE_DEX, OWNER_USER_ID, loadingPackageName);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index fd07cb0..7cd8cedd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -27,12 +27,6 @@
 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.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
@@ -78,7 +72,6 @@
     @Mock Installer mInstaller;
     @Mock IPackageManager mPM;
     private final Object mInstallLock = new Object();
-    @Mock DexManager.Listener mListener;
 
     private DexManager mDexManager;
 
@@ -114,9 +107,8 @@
         mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0,
                 DELEGATE_LAST_CLASS_LOADER_NAME);
 
-        mDexManager = new DexManager(
-            /*Context*/ null, mPM, /*PackageDexOptimizer*/ null, mInstaller, mInstallLock,
-            mListener);
+        mDexManager = new DexManager(/*Context*/ null, mPM, /*PackageDexOptimizer*/ null,
+                mInstaller, mInstallLock);
 
         // Foo and Bar are available to user0.
         // Only Bar is available to user1;
@@ -415,9 +407,10 @@
         String frameworkDex = "/system/framework/com.android.location.provider.jar";
         // Load a dex file from framework.
         notifyDexLoad(mFooUser0, Arrays.asList(frameworkDex), mUser0);
-        // The dex file should not be recognized as a package.
-        assertFalse(mDexManager.hasInfoOnPackage(frameworkDex));
-        assertNull(mDexManager.getPackageDynamicCodeInfo(frameworkDex));
+        // The dex file should not be recognized as owned by the package.
+        assertFalse(mDexManager.hasInfoOnPackage(mFooUser0.getPackageName()));
+
+        assertNull(getPackageDynamicCodeInfo(mFooUser0));
     }
 
     @Test
@@ -510,21 +503,6 @@
         assertHasDclInfo(mBarUser0, mBarUser0, secondaries);
     }
 
-    @Test
-    public void testReconcileSecondaryDexFiles_invokesListener() throws Exception {
-        List<String> fooSecondaries = mFooUser0.getSecondaryDexPathsFromProtectedDirs();
-        notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
-
-        when(mPM.getPackageInfo(mFooUser0.getPackageName(), 0, 0))
-                .thenReturn(mFooUser0.mPackageInfo);
-
-        mDexManager.reconcileSecondaryDexFiles(mFooUser0.getPackageName());
-
-        verify(mListener, times(fooSecondaries.size()))
-                .onReconcileSecondaryDexFile(any(ApplicationInfo.class),
-                        any(DexUseInfo.class), anyString(), anyInt());
-    }
-
     private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
             List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
             String[] expectedContexts) {
@@ -585,6 +563,10 @@
         return pui;
     }
 
+    private PackageDynamicCode getPackageDynamicCodeInfo(TestData testData) {
+        return mDexManager.getDexLogger().getPackageDynamicCodeInfo(testData.getPackageName());
+    }
+
     private void assertNoUseInfo(TestData testData) {
         assertFalse(mDexManager.hasInfoOnPackage(testData.getPackageName()));
     }
@@ -600,11 +582,11 @@
     }
 
     private void assertNoDclInfo(TestData testData) {
-        assertNull(mDexManager.getPackageDynamicCodeInfo(testData.getPackageName()));
+        assertNull(getPackageDynamicCodeInfo(testData));
     }
 
     private void assertNoDclInfo(TestData testData, int userId) {
-        PackageDynamicCode info = mDexManager.getPackageDynamicCodeInfo(testData.getPackageName());
+        PackageDynamicCode info = getPackageDynamicCodeInfo(testData);
         if (info == null) {
             return;
         }
@@ -615,7 +597,7 @@
     }
 
     private void assertHasDclInfo(TestData owner, TestData loader, List<String> paths) {
-        PackageDynamicCode info = mDexManager.getPackageDynamicCodeInfo(owner.getPackageName());
+        PackageDynamicCode info = getPackageDynamicCodeInfo(owner);
         assertNotNull("No DCL data for owner " + owner.getPackageName(), info);
         for (String path : paths) {
             DynamicCodeFile fileInfo = info.mFileUsageMap.get(path);
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java
index eb4cc4e..f4cdc8cd 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDynamicCodeLoadingTests.java
@@ -16,9 +16,12 @@
 
 package com.android.server.pm.dex;
 
+import static com.android.server.pm.dex.PackageDynamicCodeLoading.MAX_FILES_PER_OWNER;
 import static com.android.server.pm.dex.PackageDynamicCodeLoading.escape;
 import static com.android.server.pm.dex.PackageDynamicCodeLoading.unescape;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
@@ -119,6 +122,24 @@
     }
 
     @Test
+    public void testRecord_tooManyFiles_ignored() {
+        PackageDynamicCodeLoading info = new PackageDynamicCodeLoading();
+        int tooManyFiles = MAX_FILES_PER_OWNER + 1;
+        for (int i = 1; i <= tooManyFiles; i++) {
+            Entry entry = new Entry("owning.package", "/path/file" + i, 'D', 10, "loading.package");
+            boolean added = record(info, entry);
+            Set<Entry> entries = entriesFrom(info);
+            if (i < tooManyFiles) {
+                assertThat(entries).contains(entry);
+                assertTrue(added);
+            } else {
+                assertThat(entries).doesNotContain(entry);
+                assertFalse(added);
+            }
+        }
+    }
+
+    @Test
     public void testClear() {
         Entry[] entries = {
                 new Entry("owner1", "file1", 'D', 10, "loader1"),
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 49030f5..fb371c1 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -476,7 +476,7 @@
     /**
      * A boolean meta-data value indicating whether an {@link InCallService} implements an
      * in-call user interface to be used while the device is in car-mode (see
-     * {@link android.content.res.Configuration.UI_MODE_TYPE_CAR}).
+     * {@link android.content.res.Configuration#UI_MODE_TYPE_CAR}).
      */
     public static final String METADATA_IN_CALL_SERVICE_CAR_MODE_UI =
             "android.telecom.IN_CALL_SERVICE_CAR_MODE_UI";
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index cfe134fd..388b5fb 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1398,9 +1398,9 @@
      * Example: "default"
      *
      * {@code ERROR_CODE_1} is an integer defined in
-     * {@link com.android.internal.telephony.dataconnection.DcFailCause DcFailure}
+     * {@link DataFailCause DcFailure}
      * Example:
-     * {@link com.android.internal.telephony.dataconnection.DcFailCause#MISSING_UNKNOWN_APN}
+     * {@link DataFailCause#MISSING_UNKNOWN_APN}
      *
      * {@code CARRIER_ACTION_IDX_1} is an integer defined in
      * {@link com.android.carrierdefaultapp.CarrierActionUtils CarrierActionUtils}
diff --git a/telephony/java/android/telephony/DataFailCause.java b/telephony/java/android/telephony/DataFailCause.java
new file mode 100644
index 0000000..c6f7d0e
--- /dev/null
+++ b/telephony/java/android/telephony/DataFailCause.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      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.telephony;
+
+import android.content.Context;
+import android.os.PersistableBundle;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * Returned as the reason for a connection failure as defined
+ * by RIL_DataCallFailCause in ril.h and some local errors.
+ * @hide
+ */
+public enum DataFailCause {
+    NONE(0),
+
+    // This series of errors as specified by the standards
+    // specified in ril.h
+    OPERATOR_BARRED(0x08),                  /* no retry */
+    NAS_SIGNALLING(0x0E),
+    LLC_SNDCP(0x19),
+    INSUFFICIENT_RESOURCES(0x1A),
+    MISSING_UNKNOWN_APN(0x1B),              /* no retry */
+    UNKNOWN_PDP_ADDRESS_TYPE(0x1C),         /* no retry */
+    USER_AUTHENTICATION(0x1D),              /* no retry */
+    ACTIVATION_REJECT_GGSN(0x1E),           /* no retry */
+    ACTIVATION_REJECT_UNSPECIFIED(0x1F),
+    SERVICE_OPTION_NOT_SUPPORTED(0x20),     /* no retry */
+    SERVICE_OPTION_NOT_SUBSCRIBED(0x21),    /* no retry */
+    SERVICE_OPTION_OUT_OF_ORDER(0x22),
+    NSAPI_IN_USE(0x23),                     /* no retry */
+    REGULAR_DEACTIVATION(0x24),             /* possibly restart radio, based on config */
+    QOS_NOT_ACCEPTED(0x25),
+    NETWORK_FAILURE(0x26),
+    UMTS_REACTIVATION_REQ(0x27),
+    FEATURE_NOT_SUPP(0x28),
+    TFT_SEMANTIC_ERROR(0x29),
+    TFT_SYTAX_ERROR(0x2A),
+    UNKNOWN_PDP_CONTEXT(0x2B),
+    FILTER_SEMANTIC_ERROR(0x2C),
+    FILTER_SYTAX_ERROR(0x2D),
+    PDP_WITHOUT_ACTIVE_TFT(0x2E),
+    ONLY_IPV4_ALLOWED(0x32),                /* no retry */
+    ONLY_IPV6_ALLOWED(0x33),                /* no retry */
+    ONLY_SINGLE_BEARER_ALLOWED(0x34),
+    ESM_INFO_NOT_RECEIVED(0x35),
+    PDN_CONN_DOES_NOT_EXIST(0x36),
+    MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED(0x37),
+    MAX_ACTIVE_PDP_CONTEXT_REACHED(0x41),
+    UNSUPPORTED_APN_IN_CURRENT_PLMN(0x42),
+    INVALID_TRANSACTION_ID(0x51),
+    MESSAGE_INCORRECT_SEMANTIC(0x5F),
+    INVALID_MANDATORY_INFO(0x60),
+    MESSAGE_TYPE_UNSUPPORTED(0x61),
+    MSG_TYPE_NONCOMPATIBLE_STATE(0x62),
+    UNKNOWN_INFO_ELEMENT(0x63),
+    CONDITIONAL_IE_ERROR(0x64),
+    MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE(0x65),
+    PROTOCOL_ERRORS(0x6F),                  /* no retry */
+    APN_TYPE_CONFLICT(0x70),
+    INVALID_PCSCF_ADDR(0x71),
+    INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN(0x72),
+    EMM_ACCESS_BARRED(0x73),
+    EMERGENCY_IFACE_ONLY(0x74),
+    IFACE_MISMATCH(0x75),
+    COMPANION_IFACE_IN_USE(0x76),
+    IP_ADDRESS_MISMATCH(0x77),
+    IFACE_AND_POL_FAMILY_MISMATCH(0x78),
+    EMM_ACCESS_BARRED_INFINITE_RETRY(0x79),
+    AUTH_FAILURE_ON_EMERGENCY_CALL(0x7A),
+
+    // OEM sepecific error codes. To be used by OEMs when they don't
+    // want to reveal error code which would be replaced by ERROR_UNSPECIFIED
+    OEM_DCFAILCAUSE_1(0x1001),
+    OEM_DCFAILCAUSE_2(0x1002),
+    OEM_DCFAILCAUSE_3(0x1003),
+    OEM_DCFAILCAUSE_4(0x1004),
+    OEM_DCFAILCAUSE_5(0x1005),
+    OEM_DCFAILCAUSE_6(0x1006),
+    OEM_DCFAILCAUSE_7(0x1007),
+    OEM_DCFAILCAUSE_8(0x1008),
+    OEM_DCFAILCAUSE_9(0x1009),
+    OEM_DCFAILCAUSE_10(0x100A),
+    OEM_DCFAILCAUSE_11(0x100B),
+    OEM_DCFAILCAUSE_12(0x100C),
+    OEM_DCFAILCAUSE_13(0x100D),
+    OEM_DCFAILCAUSE_14(0x100E),
+    OEM_DCFAILCAUSE_15(0x100F),
+
+    // Local errors generated by Vendor RIL
+    // specified in ril.h
+    REGISTRATION_FAIL(-1),
+    GPRS_REGISTRATION_FAIL(-2),
+    SIGNAL_LOST(-3),                        /* no retry */
+    PREF_RADIO_TECH_CHANGED(-4),
+    RADIO_POWER_OFF(-5),                    /* no retry */
+    TETHERED_CALL_ACTIVE(-6),               /* no retry */
+    ERROR_UNSPECIFIED(0xFFFF),
+
+    // Errors generated by the Framework
+    // specified here
+    UNKNOWN(0x10000),
+    RADIO_NOT_AVAILABLE(0x10001),                   /* no retry */
+    UNACCEPTABLE_NETWORK_PARAMETER(0x10002),        /* no retry */
+    CONNECTION_TO_DATACONNECTIONAC_BROKEN(0x10003),
+    LOST_CONNECTION(0x10004),
+    RESET_BY_FRAMEWORK(0x10005);
+
+    private final int mErrorCode;
+    private static final HashMap<Integer, DataFailCause> sErrorCodeToFailCauseMap;
+    static {
+        sErrorCodeToFailCauseMap = new HashMap<Integer, DataFailCause>();
+        for (DataFailCause fc : values()) {
+            sErrorCodeToFailCauseMap.put(fc.getErrorCode(), fc);
+        }
+    }
+
+    /**
+     * Map of subId -> set of data call setup permanent failure for the carrier.
+     */
+    private static final HashMap<Integer, HashSet<DataFailCause>> sPermanentFailureCache =
+            new HashMap<>();
+
+    DataFailCause(int errorCode) {
+        mErrorCode = errorCode;
+    }
+
+    public int getErrorCode() {
+        return mErrorCode;
+    }
+
+    /**
+     * Returns whether or not the fail cause is a failure that requires a modem restart
+     *
+     * @param context device context
+     * @param subId subscription index
+     * @return true if the fail cause code needs platform to trigger a modem restart.
+     */
+    public boolean isRadioRestartFailure(Context context, int subId) {
+        CarrierConfigManager configManager = (CarrierConfigManager)
+                context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+        if (configManager != null) {
+            PersistableBundle b = configManager.getConfigForSubId(subId);
+
+            if (b != null) {
+                if (this == REGULAR_DEACTIVATION
+                        && b.getBoolean(CarrierConfigManager
+                        .KEY_RESTART_RADIO_ON_PDP_FAIL_REGULAR_DEACTIVATION_BOOL)) {
+                    // This is for backward compatibility support. We need to continue support this
+                    // old configuration until it gets removed in the future.
+                    return true;
+                }
+                // Check the current configurations.
+                int[] causeCodes = b.getIntArray(CarrierConfigManager
+                        .KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY);
+                if (causeCodes != null) {
+                    return Arrays.stream(causeCodes).anyMatch(i -> i == getErrorCode());
+                }
+            }
+        }
+
+        return false;
+    }
+
+    public boolean isPermanentFailure(Context context, int subId) {
+
+        synchronized (sPermanentFailureCache) {
+
+            HashSet<DataFailCause> permanentFailureSet = sPermanentFailureCache.get(subId);
+
+            // In case of cache miss, we need to look up the settings from carrier config.
+            if (permanentFailureSet == null) {
+                // Retrieve the permanent failure from carrier config
+                CarrierConfigManager configManager = (CarrierConfigManager)
+                        context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+                if (configManager != null) {
+                    PersistableBundle b = configManager.getConfigForSubId(subId);
+                    if (b != null) {
+                        String[] permanentFailureStrings = b.getStringArray(CarrierConfigManager.
+                                KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS);
+
+                        if (permanentFailureStrings != null) {
+                            permanentFailureSet = new HashSet<>();
+                            for (String failure : permanentFailureStrings) {
+                                permanentFailureSet.add(DataFailCause.valueOf(failure));
+                            }
+                        }
+                    }
+                }
+
+                // If we are not able to find the configuration from carrier config, use the default
+                // ones.
+                if (permanentFailureSet == null) {
+                    permanentFailureSet = new HashSet<DataFailCause>() {
+                        {
+                            add(OPERATOR_BARRED);
+                            add(MISSING_UNKNOWN_APN);
+                            add(UNKNOWN_PDP_ADDRESS_TYPE);
+                            add(USER_AUTHENTICATION);
+                            add(ACTIVATION_REJECT_GGSN);
+                            add(SERVICE_OPTION_NOT_SUPPORTED);
+                            add(SERVICE_OPTION_NOT_SUBSCRIBED);
+                            add(NSAPI_IN_USE);
+                            add(ONLY_IPV4_ALLOWED);
+                            add(ONLY_IPV6_ALLOWED);
+                            add(PROTOCOL_ERRORS);
+                            add(RADIO_POWER_OFF);
+                            add(TETHERED_CALL_ACTIVE);
+                            add(RADIO_NOT_AVAILABLE);
+                            add(UNACCEPTABLE_NETWORK_PARAMETER);
+                            add(SIGNAL_LOST);
+                        }
+                    };
+                }
+
+                sPermanentFailureCache.put(subId, permanentFailureSet);
+            }
+
+            return permanentFailureSet.contains(this);
+        }
+    }
+
+    public boolean isEventLoggable() {
+        return (this == OPERATOR_BARRED) || (this == INSUFFICIENT_RESOURCES) ||
+                (this == UNKNOWN_PDP_ADDRESS_TYPE) || (this == USER_AUTHENTICATION) ||
+                (this == ACTIVATION_REJECT_GGSN) || (this == ACTIVATION_REJECT_UNSPECIFIED) ||
+                (this == SERVICE_OPTION_NOT_SUBSCRIBED) ||
+                (this == SERVICE_OPTION_NOT_SUPPORTED) ||
+                (this == SERVICE_OPTION_OUT_OF_ORDER) || (this == NSAPI_IN_USE) ||
+                (this == ONLY_IPV4_ALLOWED) || (this == ONLY_IPV6_ALLOWED) ||
+                (this == PROTOCOL_ERRORS) || (this == SIGNAL_LOST) ||
+                (this == RADIO_POWER_OFF) || (this == TETHERED_CALL_ACTIVE) ||
+                (this == UNACCEPTABLE_NETWORK_PARAMETER);
+    }
+
+    public static DataFailCause fromInt(int errorCode) {
+        DataFailCause fc = sErrorCodeToFailCauseMap.get(errorCode);
+        if (fc == null) {
+            fc = UNKNOWN;
+        }
+        return fc;
+    }
+}
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index c95837e..4dcb410 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -20,7 +20,6 @@
 import com.android.i18n.phonenumbers.PhoneNumberUtil;
 import com.android.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberFormat;
 import com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
-import com.android.i18n.phonenumbers.ShortNumberInfo;
 
 import android.annotation.IntDef;
 import android.annotation.UnsupportedAppUsage;
@@ -2185,101 +2184,6 @@
     }
 
     /**
-     * Back-up old logics for {@link #isEmergencyNumberInternal} for legacy and deprecate purpose.
-     *
-     * @hide
-     */
-    public static boolean isEmergencyNumberInternal(String number, boolean useExactMatch,
-                                                    String defaultCountryIso) {
-        // If the number passed in is null, just return false:
-        if (number == null) return false;
-
-        // If the number passed in is a SIP address, return false, since the
-        // concept of "emergency numbers" is only meaningful for calls placed
-        // over the cell network.
-        // (Be sure to do this check *before* calling extractNetworkPortionAlt(),
-        // since the whole point of extractNetworkPortionAlt() is to filter out
-        // any non-dialable characters (which would turn 'abc911def@example.com'
-        // into '911', for example.))
-        if (PhoneNumberUtils.isUriNumber(number)) {
-            return false;
-        }
-
-        // Strip the separators from the number before comparing it
-        // to the list.
-        number = PhoneNumberUtils.extractNetworkPortionAlt(number);
-
-        String emergencyNumbers = "";
-        int slotId = SubscriptionManager.getSlotIndex(getDefaultVoiceSubId());
-
-        // retrieve the list of emergency numbers
-        // check read-write ecclist property first
-        String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
-
-        emergencyNumbers = SystemProperties.get(ecclist, "");
-
-        Rlog.d(LOG_TAG, "slotId:" + slotId + " country:"
-                + defaultCountryIso + " emergencyNumbers: " +  emergencyNumbers);
-
-        if (TextUtils.isEmpty(emergencyNumbers)) {
-            // then read-only ecclist property since old RIL only uses this
-            emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
-        }
-
-        if (!TextUtils.isEmpty(emergencyNumbers)) {
-            // searches through the comma-separated list for a match,
-            // return true if one is found.
-            for (String emergencyNum : emergencyNumbers.split(",")) {
-                // It is not possible to append additional digits to an emergency number to dial
-                // the number in Brazil - it won't connect.
-                if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) {
-                    if (number.equals(emergencyNum)) {
-                        return true;
-                    }
-                } else {
-                    if (number.startsWith(emergencyNum)) {
-                        return true;
-                    }
-                }
-            }
-            // no matches found against the list!
-            return false;
-        }
-
-        Rlog.d(LOG_TAG, "System property doesn't provide any emergency numbers."
-                + " Use embedded logic for determining ones.");
-
-        // If slot id is invalid, means that there is no sim card.
-        // According spec 3GPP TS22.101, the following numbers should be
-        // ECC numbers when SIM/USIM is not present.
-        emergencyNumbers = ((slotId < 0) ? "112,911,000,08,110,118,119,999" : "112,911");
-
-        for (String emergencyNum : emergencyNumbers.split(",")) {
-            if (useExactMatch) {
-                if (number.equals(emergencyNum)) {
-                    return true;
-                }
-            } else {
-                if (number.startsWith(emergencyNum)) {
-                    return true;
-                }
-            }
-        }
-
-        // No ecclist system property, so use our own list.
-        if (defaultCountryIso != null) {
-            ShortNumberInfo info = ShortNumberInfo.getInstance();
-            if (useExactMatch) {
-                return info.isEmergencyNumber(number, defaultCountryIso);
-            } else {
-                return info.connectsToEmergencyNumber(number, defaultCountryIso);
-            }
-        }
-
-        return false;
-    }
-
-    /**
      * isVoiceMailNumber: checks a given number against the voicemail
      *   number provided by the RIL and SIM card. The caller must have
      *   the READ_PHONE_STATE credential.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 348ab2a..11b6674 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -5629,7 +5629,7 @@
         if (value == null) {
             value = "";
         }
-
+        value.replace(',', ' ');
         if (prop != null) {
             p = prop.split(",");
         }
@@ -5655,7 +5655,13 @@
             }
         }
 
-        if (propVal.length() > SystemProperties.PROP_VALUE_MAX) {
+        int propValLen = propVal.length();
+        try {
+            propValLen = propVal.getBytes("utf-8").length;
+        } catch (java.io.UnsupportedEncodingException e) {
+            Rlog.d(TAG, "setTelephonyProperty: utf-8 not supported");
+        }
+        if (propValLen > SystemProperties.PROP_VALUE_MAX) {
             Rlog.d(TAG, "setTelephonyProperty: property too long phoneId=" + phoneId +
                     " property=" + property + " value: " + value + " propVal=" + propVal);
             return;
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index f124595..4d95e55 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -331,7 +331,80 @@
      */
     public static final int CODE_SIP_USER_MARKED_UNWANTED = 365;
 
-    /*
+    /**
+     * SIP Response : 405
+     * Method not allowed for the address in the Request URI
+     */
+    public static final int CODE_SIP_METHOD_NOT_ALLOWED = 366;
+
+    /**
+     * SIP Response : 407
+     * The request requires user authentication
+     */
+    public static final int CODE_SIP_PROXY_AUTHENTICATION_REQUIRED = 367;
+
+    /**
+     * SIP Response : 413
+     * Request body too large
+     */
+    public static final int CODE_SIP_REQUEST_ENTITY_TOO_LARGE = 368;
+
+    /**
+     * SIP Response : 414
+     * Request-URI too large
+     */
+    public static final int CODE_SIP_REQUEST_URI_TOO_LARGE = 369;
+
+    /**
+     * SIP Response : 421
+     * Specific extension is required, which is not present in the HEADER
+     */
+    public static final int CODE_SIP_EXTENSION_REQUIRED = 370;
+
+    /**
+     * SIP Response : 422
+     * The session expiration field too small
+     */
+    public static final int CODE_SIP_INTERVAL_TOO_BRIEF = 371;
+
+    /**
+     * SIP Response : 481
+     * Request received by the server does not match any dialog or transaction
+     */
+    public static final int CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST = 372;
+
+    /**
+     * SIP Response : 482
+     * Server has detected a loop
+     */
+    public static final int CODE_SIP_LOOP_DETECTED = 373;
+
+    /**
+     * SIP Response : 483
+     * Max-Forwards value reached
+     */
+    public static final int CODE_SIP_TOO_MANY_HOPS = 374;
+
+    /**
+     * SIP Response : 485
+     * Request-URI is ambiguous
+     *
+     */
+    public static final int CODE_SIP_AMBIGUOUS = 376;
+
+    /**
+     * SIP Response : 491
+     * Server has pending request for same dialog
+     */
+    public static final int CODE_SIP_REQUEST_PENDING = 377;
+
+    /**
+     * SIP Response : 493
+     * The request cannot be decrypted by recipient
+     */
+    public static final int CODE_SIP_UNDECIPHERABLE = 378;
+
+    /**
      * MEDIA (IMS -> Telephony)
      */
     /**
@@ -384,6 +457,24 @@
      * The call has been terminated by the network or remote user.
      */
     public static final int CODE_USER_TERMINATED_BY_REMOTE = 510;
+    /**
+    * Upgrade Downgrade request rejected by
+    * Remote user if the request is MO initiated
+    * Local user if the request is MT initiated
+    */
+    public static final int CODE_USER_REJECTED_SESSION_MODIFICATION = 511;
+
+    /**
+    * Upgrade Downgrade request cacncelled by the user who initiated it
+    */
+    public static final int CODE_USER_CANCELLED_SESSION_MODIFICATION = 512;
+
+    /**
+     * UPGRADE DOWNGRADE operation failed
+     * This can happen due to failure from SIP/RTP/SDP generation or a Call end is
+     * triggered/received while Reinvite is in progress.
+     */
+    public static final int CODE_SESSION_MODIFICATION_FAILED = 1517;
 
     /*
      * UT
@@ -484,6 +575,16 @@
     public static final int CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 1100;
 
     /**
+     * For MultiEndPoint - Call was rejected elsewhere
+     */
+    public static final int CODE_REJECTED_ELSEWHERE = 1017;
+
+    /**
+     * Supplementary services (HOLD/RESUME) failure error codes.
+     * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
+     */
+
+    /**
      * Supplementary Services (HOLD/RESUME) - the command failed.
      */
     public static final int CODE_SUPP_SVC_FAILED = 1201;
diff --git a/telephony/java/android/telephony/ims/RcsManager.java b/telephony/java/android/telephony/ims/RcsManager.java
new file mode 100644
index 0000000..d50b516
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsManager.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2018 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.telephony.ims;
+
+import android.annotation.SystemService;
+import android.content.Context;
+
+/**
+ * The manager class for RCS related utilities.
+ * @hide
+ */
+@SystemService(Context.TELEPHONY_RCS_SERVICE)
+public class RcsManager {
+
+    private static final RcsMessageStore sRcsMessageStoreInstance = new RcsMessageStore();
+
+    /**
+     * Returns an instance of RcsMessageStore.
+     */
+    public RcsMessageStore getRcsMessageStore() {
+        return sRcsMessageStoreInstance;
+    }
+}
diff --git a/telephony/java/android/telephony/rcs/RcsManager.java b/telephony/java/android/telephony/ims/RcsMessageStore.java
similarity index 78%
rename from telephony/java/android/telephony/rcs/RcsManager.java
rename to telephony/java/android/telephony/ims/RcsMessageStore.java
index 0ef4e15..c89c0be 100644
--- a/telephony/java/android/telephony/rcs/RcsManager.java
+++ b/telephony/java/android/telephony/ims/RcsMessageStore.java
@@ -14,24 +14,20 @@
  * limitations under the License.
  */
 
-package android.telephony.rcs;
+package android.telephony.ims;
 
-import android.annotation.SystemService;
-import android.content.Context;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.telephony.Rlog;
-
-import com.android.internal.telephony.rcs.IRcs;
+import android.telephony.ims.aidl.IRcs;
 
 /**
- * RcsManager is the application interface to RcsProvider and provides access methods to
+ * RcsMessageStore is the application interface to RcsProvider and provides access methods to
  * RCS related database tables.
  * @hide - TODO make this public
  */
-@SystemService(Context.TELEPHONY_RCS_SERVICE)
-public class RcsManager {
-    private static final String TAG = "RcsManager";
+public class RcsMessageStore {
+    private static final String TAG = "RcsMessageStore";
     private static final boolean VDBG = false;
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/rcs/IRcs.aidl b/telephony/java/android/telephony/ims/RcsThread.aidl
similarity index 63%
copy from telephony/java/com/android/internal/telephony/rcs/IRcs.aidl
copy to telephony/java/android/telephony/ims/RcsThread.aidl
index 4c289ac..79d47326 100644
--- a/telephony/java/com/android/internal/telephony/rcs/IRcs.aidl
+++ b/telephony/java/android/telephony/ims/RcsThread.aidl
@@ -1,11 +1,12 @@
 /*
- * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Copyright 2018, 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
+ *     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,
@@ -14,12 +15,6 @@
  * limitations under the License.
  */
 
-package com.android.internal.telephony.rcs;
+package android.telephony;
 
-interface IRcs {
-    // RcsManager APIs
-    void deleteThread(int threadId);
-
-    // RcsThread APIs
-    int getMessageCount(int rcsThreadId);
-}
\ No newline at end of file
+parcelable RcsThread;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/rcs/RcsThread.java b/telephony/java/android/telephony/ims/RcsThread.java
similarity index 96%
rename from telephony/java/android/telephony/rcs/RcsThread.java
rename to telephony/java/android/telephony/ims/RcsThread.java
index 83eb973..b7f440d 100644
--- a/telephony/java/android/telephony/rcs/RcsThread.java
+++ b/telephony/java/android/telephony/ims/RcsThread.java
@@ -14,14 +14,13 @@
  * limitations under the License.
  */
 
-package android.telephony.rcs;
+package android.telephony.ims;
 
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-
-import com.android.internal.telephony.rcs.IRcs;
+import android.telephony.ims.aidl.IRcs;
 
 /**
  * RcsThread represents a single RCS conversation thread. It holds messages that were sent and
diff --git a/telephony/java/com/android/internal/telephony/rcs/IRcs.aidl b/telephony/java/android/telephony/ims/aidl/IRcs.aidl
similarity index 83%
rename from telephony/java/com/android/internal/telephony/rcs/IRcs.aidl
rename to telephony/java/android/telephony/ims/aidl/IRcs.aidl
index 4c289ac..b2e2fad 100644
--- a/telephony/java/com/android/internal/telephony/rcs/IRcs.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IRcs.aidl
@@ -14,10 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.internal.telephony.rcs;
+package android.telephony.ims.aidl;
 
+/**
+ * RPC definition between RCS storage APIs and phone process.
+ * {@hide}
+ */
 interface IRcs {
-    // RcsManager APIs
+    // RcsMessageStore APIs
     void deleteThread(int threadId);
 
     // RcsThread APIs
diff --git a/telephony/java/android/telephony/rcs/RcsThread.aidl b/telephony/java/android/telephony/rcs/RcsThread.aidl
deleted file mode 100644
index e2e0da5d..0000000
--- a/telephony/java/android/telephony/rcs/RcsThread.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-**
-** Copyright 2018, 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.telephony;
-
-parcelable RcsThread;
\ No newline at end of file
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index c934317..2ebe870 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -421,6 +421,7 @@
     int RIL_REQUEST_SET_SIGNAL_STRENGTH_REPORTING_CRITERIA = 202;
     int RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA = 203;
     int RIL_REQUEST_SET_PREFERRED_DATA_MODEM = 204;
+    int RIL_REQUEST_EMERGENCY_DIAL = 205;
 
     /* Responses begin */
     int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index eed8ae7..5ea8ff1 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -39,7 +39,6 @@
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
@@ -840,6 +839,7 @@
                 /* SAMPLE OUTPUT : Cold launch
                 Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
                 Status: ok
+                LaunchState: COLD
                 Activity: com.google.android.calculator/com.android.calculator2.Calculator
                 TotalTime: 357
                 WaitTime: 377
@@ -848,6 +848,7 @@
                 Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator }
                 Warning: Activity not started, its current task has been brought to the front
                 Status: ok
+                LaunchState: HOT
                 Activity: com.google.android.calculator/com.android.calculator2.CalculatorGoogle
                 TotalTime: 60
                 WaitTime: 67
diff --git a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java
index d8b3b20..75ee089 100644
--- a/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java
+++ b/tests/DexLoggerIntegrationTests/src/com/android/server/pm/dex/DexLoggerIntegrationTests.java
@@ -18,20 +18,23 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import android.app.UiAutomation;
 import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.LargeTest;
 import android.util.EventLog;
+
 import dalvik.system.DexClassLoader;
 
-import org.junit.After;
-import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
+import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.InputStream;
@@ -40,6 +43,7 @@
 import java.util.ArrayList;
 import java.util.Formatter;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Integration tests for {@link com.android.server.pm.dex.DexLogger}.
@@ -47,10 +51,10 @@
  * The setup for the test dynamically loads code in a jar extracted
  * from our assets (a secondary dex file).
  *
- * We then use adb to trigger secondary dex file reconcilation (and
- * wait for it to complete). As a side-effect of this DexLogger should
- * be notified of the file and should log the hash of the file's name
- * and content.  We verify that this message appears in the event log.
+ * We then use shell commands to trigger dynamic code logging (and wait
+ * for it to complete). This causes DexLogger to log the hash of the
+ * file's name and content.  We verify that this message appears in
+ * the event log.
  *
  * Run with "atest DexLoggerIntegrationTests".
  */
@@ -58,29 +62,89 @@
 @RunWith(JUnit4.class)
 public final class DexLoggerIntegrationTests {
 
-    private static final String PACKAGE_NAME = "com.android.frameworks.dexloggertest";
-
     // Event log tag used for SNET related events
     private static final int SNET_TAG = 0x534e4554;
+
     // Subtag used to distinguish dynamic code loading events
     private static final String DCL_SUBTAG = "dcl";
 
-    // Obtained via "echo -n copied.jar | sha256sum"
-    private static final String EXPECTED_NAME_HASH =
-            "1B6C71DB26F36582867432CCA12FB6A517470C9F9AABE9198DD4C5C030D6DC0C";
+    // All the tags we care about
+    private static final int[] TAG_LIST = new int[] { SNET_TAG };
 
-    private static String expectedContentHash;
+    // This is {@code DynamicCodeLoggingService#JOB_ID}
+    private static final int DYNAMIC_CODE_LOGGING_JOB_ID = 2030028;
+
+    private static Context sContext;
+    private static int sMyUid;
 
     @BeforeClass
-    public static void setUpAll() throws Exception {
-        Context context = InstrumentationRegistry.getTargetContext();
+    public static void setUpAll() {
+        sContext = InstrumentationRegistry.getTargetContext();
+        sMyUid = android.os.Process.myUid();
+    }
+
+    @Before
+    public void primeEventLog() {
+        // Force a round trip to logd to make sure everything is up to date.
+        // Without this the first test passes and others don't - we don't see new events in the
+        // log. The exact reason is unclear.
+        EventLog.writeEvent(SNET_TAG, "Dummy event");
+    }
+
+    @Test
+    public void testDexLoggerGeneratesEvents() throws Exception {
+        File privateCopyFile = fileForJar("copied.jar");
+        // Obtained via "echo -n copied.jar | sha256sum"
+        String expectedNameHash =
+                "1B6C71DB26F36582867432CCA12FB6A517470C9F9AABE9198DD4C5C030D6DC0C";
+        String expectedContentHash = copyAndHashJar(privateCopyFile);
+
+        // Feed the jar to a class loader and make sure it contains what we expect.
+        ClassLoader parentClassLoader = sContext.getClass().getClassLoader();
+        ClassLoader loader =
+                new DexClassLoader(privateCopyFile.toString(), null, null, parentClassLoader);
+        loader.loadClass("com.android.dcl.Simple");
+
+        // And make sure we log events about it
+        long previousEventNanos = mostRecentEventTimeNanos();
+        runDexLogger();
+
+        assertDclLoggedSince(previousEventNanos, expectedNameHash, expectedContentHash);
+    }
+
+    @Test
+
+    public void testDexLoggerGeneratesEvents_unknownClassLoader() throws Exception {
+        File privateCopyFile = fileForJar("copied2.jar");
+        String expectedNameHash =
+                "202158B6A3169D78F1722487205A6B036B3F2F5653FDCFB4E74710611AC7EB93";
+        String expectedContentHash = copyAndHashJar(privateCopyFile);
+
+        // This time make sure an unknown class loader is an ancestor of the class loader we use.
+        ClassLoader knownClassLoader = sContext.getClass().getClassLoader();
+        ClassLoader unknownClassLoader = new UnknownClassLoader(knownClassLoader);
+        ClassLoader loader =
+                new DexClassLoader(privateCopyFile.toString(), null, null, unknownClassLoader);
+        loader.loadClass("com.android.dcl.Simple");
+
+        // And make sure we log events about it
+        long previousEventNanos = mostRecentEventTimeNanos();
+        runDexLogger();
+
+        assertDclLoggedSince(previousEventNanos, expectedNameHash, expectedContentHash);
+    }
+
+    private static File fileForJar(String name) {
+        return new File(sContext.getDir("jars", Context.MODE_PRIVATE), name);
+    }
+
+    private static String copyAndHashJar(File copyTo) throws Exception {
         MessageDigest hasher = MessageDigest.getInstance("SHA-256");
 
         // Copy the jar from our Java resources to a private data directory
-        File privateCopy = new File(context.getDir("jars", Context.MODE_PRIVATE), "copied.jar");
         Class<?> thisClass = DexLoggerIntegrationTests.class;
         try (InputStream input = thisClass.getResourceAsStream("/javalib.jar");
-                OutputStream output = new FileOutputStream(privateCopy)) {
+                OutputStream output = new FileOutputStream(copyTo)) {
             byte[] buffer = new byte[1024];
             while (true) {
                 int numRead = input.read(buffer);
@@ -92,42 +156,63 @@
             }
         }
 
-        // Remember the SHA-256 of the file content to check that it is the same as
-        // the value we see logged.
+        // Compute the SHA-256 of the file content so we can check that it is the same as the value
+        // we see logged.
         Formatter formatter = new Formatter();
         for (byte b : hasher.digest()) {
             formatter.format("%02X", b);
         }
-        expectedContentHash = formatter.toString();
 
-        // Feed the jar to a class loader and make sure it contains what we expect.
-        ClassLoader loader =
-                new DexClassLoader(
-                    privateCopy.toString(), null, null, context.getClass().getClassLoader());
-        loader.loadClass("com.android.dcl.Simple");
+        return formatter.toString();
     }
 
-    @Test
-    public void testDexLoggerReconcileGeneratesEvents() throws Exception {
-        int[] tagList = new int[] { SNET_TAG };
+    private static long mostRecentEventTimeNanos() throws Exception {
         List<EventLog.Event> events = new ArrayList<>();
 
-        // There may already be events in the event log - figure out the most recent one
-        EventLog.readEvents(tagList, events);
-        long previousEventNanos =
-                events.isEmpty() ? 0 : events.get(events.size() - 1).getTimeNanos();
-        events.clear();
+        EventLog.readEvents(TAG_LIST, events);
+        return events.isEmpty() ? 0 : events.get(events.size() - 1).getTimeNanos();
+    }
 
-        Process process = Runtime.getRuntime().exec(
-            "cmd package reconcile-secondary-dex-files " + PACKAGE_NAME);
-        int exitCode = process.waitFor();
-        assertThat(exitCode).isEqualTo(0);
+    private static void runDexLogger() throws Exception {
+        // This forces {@code DynamicCodeLoggingService} to start now.
+        runCommand("cmd jobscheduler run -f android " + DYNAMIC_CODE_LOGGING_JOB_ID);
+        // Wait for the job to have run.
+        long startTime = SystemClock.elapsedRealtime();
+        while (true) {
+            String response = runCommand(
+                    "cmd jobscheduler get-job-state android " + DYNAMIC_CODE_LOGGING_JOB_ID);
+            if (!response.contains("pending") && !response.contains("active")) {
+                break;
+            }
+            if (SystemClock.elapsedRealtime() - startTime > TimeUnit.SECONDS.toMillis(10)) {
+                throw new AssertionError("Job has not completed: " + response);
+            }
+            SystemClock.sleep(100);
+        }
+    }
 
-        int myUid = android.os.Process.myUid();
-        String expectedMessage = EXPECTED_NAME_HASH + " " + expectedContentHash;
+    private static String runCommand(String command) throws Exception {
+        ByteArrayOutputStream response = new ByteArrayOutputStream();
+        byte[] buffer = new byte[1000];
+        UiAutomation ui = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        ParcelFileDescriptor fd = ui.executeShellCommand(command);
+        try (InputStream input = new ParcelFileDescriptor.AutoCloseInputStream(fd)) {
+            while (true) {
+                int count = input.read(buffer);
+                if (count == -1) {
+                    break;
+                }
+                response.write(buffer, 0, count);
+            }
+        }
+        return response.toString("UTF-8");
+    }
 
-        EventLog.readEvents(tagList, events);
-        boolean found = false;
+    private static void assertDclLoggedSince(long previousEventNanos, String expectedNameHash,
+            String expectedContentHash) throws Exception {
+        List<EventLog.Event> events = new ArrayList<>();
+        EventLog.readEvents(TAG_LIST, events);
+        int found = 0;
         for (EventLog.Event event : events) {
             if (event.getTimeNanos() <= previousEventNanos) {
                 continue;
@@ -140,15 +225,28 @@
                 continue;
             }
             int uid = (int) data[1];
-            if (uid != myUid) {
+            if (uid != sMyUid) {
                 continue;
             }
 
             String message = (String) data[2];
-            assertThat(message).isEqualTo(expectedMessage);
-            found = true;
+            if (!message.startsWith(expectedNameHash)) {
+                continue;
+            }
+
+            assertThat(message).endsWith(expectedContentHash);
+            ++found;
         }
 
-        assertThat(found).isTrue();
+        assertThat(found).isEqualTo(1);
+    }
+
+    /**
+     * A class loader that does nothing useful, but importantly doesn't extend BaseDexClassLoader.
+     */
+    private static class UnknownClassLoader extends ClassLoader {
+        UnknownClassLoader(ClassLoader parent) {
+            super(parent);
+        }
     }
 }
diff --git a/tests/RcsTests/src/com/android/tests/rcs/RcsManagerTest.java b/tests/RcsTests/src/com/android/tests/rcs/RcsMessageStoreTest.java
similarity index 83%
rename from tests/RcsTests/src/com/android/tests/rcs/RcsManagerTest.java
rename to tests/RcsTests/src/com/android/tests/rcs/RcsMessageStoreTest.java
index 7f5f03e0d..290e04c 100644
--- a/tests/RcsTests/src/com/android/tests/rcs/RcsManagerTest.java
+++ b/tests/RcsTests/src/com/android/tests/rcs/RcsMessageStoreTest.java
@@ -16,17 +16,17 @@
 package com.android.tests.rcs;
 
 import android.support.test.runner.AndroidJUnit4;
-import android.telephony.rcs.RcsManager;
+import android.telephony.ims.RcsMessageStore;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
 @RunWith(AndroidJUnit4.class)
-public class RcsManagerTest {
+public class RcsMessageStoreTest {
     //TODO(sahinc): Add meaningful tests once we have more of the implementation in place
     @Test
     public void testDeleteThreadDoesntCrash() {
-        RcsManager mRcsManager = new RcsManager();
-        mRcsManager.deleteThread(0);
+        RcsMessageStore mRcsMessageStore = new RcsMessageStore();
+        mRcsMessageStore.deleteThread(0);
     }
 }
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index 9838020..151b559 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -46,6 +46,7 @@
 import android.system.ErrnoException;
 import android.system.Os;
 import android.text.format.DateUtils;
+import android.util.Log;
 import com.android.frameworks.tests.net.R;
 import com.android.internal.util.HexDump;
 import java.io.File;
@@ -89,6 +90,7 @@
         System.loadLibrary("frameworksnettestsjni");
     }
 
+    private static final String TAG = "ApfTest";
     // Expected return codes from APF interpreter.
     private static final int PASS = 1;
     private static final int DROP = 0;
@@ -869,6 +871,37 @@
         }
     }
 
+    /**
+     * Generate APF program, run pcap file though APF filter, then check all the packets in the file
+     * should be dropped.
+     */
+    @Test
+    public void testApfFilterPcapFile() throws Exception {
+        final byte[] MOCK_PCAP_IPV4_ADDR = {(byte) 172, 16, 7, (byte) 151};
+        String pcapFilename = stageFile(R.raw.apfPcap);
+        MockIpClientCallback ipClientCallback = new MockIpClientCallback();
+        LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_PCAP_IPV4_ADDR), 16);
+        LinkProperties lp = new LinkProperties();
+        lp.addLinkAddress(link);
+
+        ApfConfiguration config = getDefaultConfig();
+        ApfCapabilities MOCK_APF_PCAP_CAPABILITIES = new ApfCapabilities(4, 1700, ARPHRD_ETHER);
+        config.apfCapabilities = MOCK_APF_PCAP_CAPABILITIES;
+        config.multicastFilter = DROP_MULTICAST;
+        config.ieee802_3Filter = DROP_802_3_FRAMES;
+        TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
+        apfFilter.setLinkProperties(lp);
+        byte[] program = ipClientCallback.getApfProgram();
+        byte[] data = new byte[ApfFilter.Counter.totalSize()];
+        final boolean result;
+
+        result = dropsAllPackets(program, data, pcapFilename);
+        Log.i(TAG, "testApfFilterPcapFile(): Data counters: " + HexDump.toHexString(data, false));
+
+        assertTrue("Failed to drop all packets by filter. \nAPF counters:" +
+            HexDump.toHexString(data, false), result);
+    }
+
     private class MockIpClientCallback extends IpClient.Callback {
         private final ConditionVariable mGotApfProgram = new ConditionVariable();
         private byte[] mLastApfProgram;
@@ -1015,12 +1048,17 @@
             4,    // Protocol size: 4
             0, 2  // Opcode: reply (2)
     };
-    private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
+    private static final int ARP_SOURCE_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 14;
+    private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ARP_HEADER_OFFSET + 24;
 
     private static final byte[] MOCK_IPV4_ADDR           = {10, 0, 0, 1};
     private static final byte[] MOCK_BROADCAST_IPV4_ADDR = {10, 0, 31, (byte) 255}; // prefix = 19
     private static final byte[] MOCK_MULTICAST_IPV4_ADDR = {(byte) 224, 0, 0, 1};
     private static final byte[] ANOTHER_IPV4_ADDR        = {10, 0, 0, 2};
+    private static final byte[] IPV4_SOURCE_ADDR         = {10, 0, 0, 3};
+    private static final byte[] ANOTHER_IPV4_SOURCE_ADDR = {(byte) 192, 0, 2, 1};
+    private static final byte[] BUG_PROBE_SOURCE_ADDR1   = {0, 0, 1, 2};
+    private static final byte[] BUG_PROBE_SOURCE_ADDR2   = {3, 4, 0, 0};
     private static final byte[] IPV4_ANY_HOST_ADDR       = {0, 0, 0, 0};
 
     // Helper to initialize a default apfFilter.
@@ -1366,10 +1404,16 @@
         assertVerdict(filterResult, program, arpRequestBroadcast(ANOTHER_IPV4_ADDR));
         assertDrop(program, arpRequestBroadcast(IPV4_ANY_HOST_ADDR));
 
+        // Verify ARP reply packets from different source ip
+        assertDrop(program, arpReply(IPV4_ANY_HOST_ADDR, IPV4_ANY_HOST_ADDR));
+        assertPass(program, arpReply(ANOTHER_IPV4_SOURCE_ADDR, IPV4_ANY_HOST_ADDR));
+        assertPass(program, arpReply(BUG_PROBE_SOURCE_ADDR1, IPV4_ANY_HOST_ADDR));
+        assertPass(program, arpReply(BUG_PROBE_SOURCE_ADDR2, IPV4_ANY_HOST_ADDR));
+
         // Verify unicast ARP reply packet is always accepted.
-        assertPass(program, arpReplyUnicast(MOCK_IPV4_ADDR));
-        assertPass(program, arpReplyUnicast(ANOTHER_IPV4_ADDR));
-        assertPass(program, arpReplyUnicast(IPV4_ANY_HOST_ADDR));
+        assertPass(program, arpReply(IPV4_SOURCE_ADDR, MOCK_IPV4_ADDR));
+        assertPass(program, arpReply(IPV4_SOURCE_ADDR, ANOTHER_IPV4_ADDR));
+        assertPass(program, arpReply(IPV4_SOURCE_ADDR, IPV4_ANY_HOST_ADDR));
 
         // Verify GARP reply packets are always filtered
         assertDrop(program, garpReply());
@@ -1398,19 +1442,20 @@
         apfFilter.shutdown();
     }
 
-    private static byte[] arpRequestBroadcast(byte[] tip) {
+    private static byte[] arpReply(byte[] sip, byte[] tip) {
         ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
         packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
-        put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS);
         put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REPLY_HEADER);
+        put(packet, ARP_SOURCE_IP_ADDRESS_OFFSET, sip);
         put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, tip);
         return packet.array();
     }
 
-    private static byte[] arpReplyUnicast(byte[] tip) {
+    private static byte[] arpRequestBroadcast(byte[] tip) {
         ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
         packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_ARP);
-        put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REPLY_HEADER);
+        put(packet, ETH_DEST_ADDR_OFFSET, ETH_BROADCAST_MAC_ADDRESS);
+        put(packet, ARP_HEADER_OFFSET, ARP_IPV4_REQUEST_HEADER);
         put(packet, ARP_TARGET_IP_ADDRESS_OFFSET, tip);
         return packet.array();
     }
@@ -1706,6 +1751,14 @@
     private native static boolean compareBpfApf(String filter, String pcap_filename,
             byte[] apf_program);
 
+
+    /**
+     * Open packet capture file {@code pcapFilename} and run it through APF filter. Then
+     * checks whether all the packets are dropped and populates data[] {@code data} with
+     * the APF counters.
+     */
+    private native static boolean dropsAllPackets(byte[] program, byte[] data, String pcapFilename);
+
     @Test
     public void testBroadcastAddress() throws Exception {
         assertEqualsIp("255.255.255.255", ApfFilter.ipv4BroadcastAddress(IPV4_ANY_HOST_ADDR, 0));
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 8081812..bca9be7 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -71,7 +71,6 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
-import android.net.NetworkRequest;
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
@@ -130,10 +129,6 @@
     private static final String TEST_USB_IFNAME = "test_rndis0";
     private static final String TEST_WLAN_IFNAME = "test_wlan0";
 
-    // Actual contents of the request don't matter for this test. The lack of
-    // any specific TRANSPORT_* is sufficient to identify this request.
-    private static final NetworkRequest mDefaultRequest = new NetworkRequest.Builder().build();
-
     @Mock private ApplicationInfo mApplicationInfo;
     @Mock private Context mContext;
     @Mock private INetworkManagementService mNMService;
@@ -257,11 +252,6 @@
             isTetheringSupportedCalls++;
             return true;
         }
-
-        @Override
-        public NetworkRequest getDefaultNetworkRequest() {
-            return mDefaultRequest;
-        }
     }
 
     private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6,
@@ -496,7 +486,7 @@
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
         verifyNoMoreInteractions(mWifiManager);
         verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_LOCAL_ONLY);
-        verify(mUpstreamNetworkMonitor, times(1)).start(any(NetworkRequest.class));
+        verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
         // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast().
         assertTrue(1 <= mTetheringDependencies.isTetheringSupportedCalls);
 
@@ -730,7 +720,7 @@
                 TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED);
         verifyNoMoreInteractions(mWifiManager);
         verifyTetheringBroadcast(TEST_WLAN_IFNAME, EXTRA_ACTIVE_TETHER);
-        verify(mUpstreamNetworkMonitor, times(1)).start(any(NetworkRequest.class));
+        verify(mUpstreamNetworkMonitor, times(1)).startObserveAllNetworks();
         // In tethering mode, in the default configuration, an explicit request
         // for a mobile network is also made.
         verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest();
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index a22cbd4..0afd607 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -24,6 +24,7 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -53,7 +54,6 @@
 import android.net.NetworkRequest;
 import android.net.NetworkState;
 import android.net.util.SharedLog;
-
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
@@ -65,7 +65,6 @@
 import org.junit.runner.RunWith;
 import org.junit.Test;
 import org.mockito.Mock;
-import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
@@ -126,7 +125,7 @@
     }
 
     @Test
-    public void testDoesNothingBeforeStarted() {
+    public void testDoesNothingBeforeTrackDefaultAndStarted() throws Exception {
         assertTrue(mCM.hasNoCallbacks());
         assertFalse(mUNM.mobileNetworkRequested());
 
@@ -138,37 +137,40 @@
 
     @Test
     public void testDefaultNetworkIsTracked() throws Exception {
-        assertEquals(0, mCM.trackingDefault.size());
+        assertTrue(mCM.hasNoCallbacks());
+        mUNM.startTrackDefaultNetwork(mDefaultRequest);
 
-        mUNM.start(mDefaultRequest);
+        mUNM.startObserveAllNetworks();
         assertEquals(1, mCM.trackingDefault.size());
 
         mUNM.stop();
-        assertTrue(mCM.hasNoCallbacks());
+        assertTrue(mCM.onlyHasDefaultCallbacks());
     }
 
     @Test
     public void testListensForAllNetworks() throws Exception {
         assertTrue(mCM.listening.isEmpty());
 
-        mUNM.start(mDefaultRequest);
+        mUNM.startTrackDefaultNetwork(mDefaultRequest);
+        mUNM.startObserveAllNetworks();
         assertFalse(mCM.listening.isEmpty());
         assertTrue(mCM.isListeningForAll());
 
         mUNM.stop();
-        assertTrue(mCM.hasNoCallbacks());
+        assertTrue(mCM.onlyHasDefaultCallbacks());
     }
 
     @Test
     public void testCallbacksRegistered() {
-        mUNM.start(mDefaultRequest);
-        verify(mCM, times(1)).registerNetworkCallback(
-                any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
+        mUNM.startTrackDefaultNetwork(mDefaultRequest);
         verify(mCM, times(1)).requestNetwork(
                 eq(mDefaultRequest), any(NetworkCallback.class), any(Handler.class));
+        mUNM.startObserveAllNetworks();
+        verify(mCM, times(1)).registerNetworkCallback(
+                any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
 
         mUNM.stop();
-        verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
+        verify(mCM, times(1)).unregisterNetworkCallback(any(NetworkCallback.class));
     }
 
     @Test
@@ -176,7 +178,7 @@
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
 
-        mUNM.start(mDefaultRequest);
+        mUNM.startObserveAllNetworks();
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
 
@@ -199,11 +201,9 @@
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
 
-        mUNM.start(mDefaultRequest);
+        mUNM.startObserveAllNetworks();
         verify(mCM, times(1)).registerNetworkCallback(
                 any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
-        verify(mCM, times(1)).requestNetwork(
-                eq(mDefaultRequest), any(NetworkCallback.class), any(Handler.class));
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
 
@@ -227,7 +227,7 @@
         assertTrue(mCM.isDunRequested());
 
         mUNM.stop();
-        verify(mCM, times(3)).unregisterNetworkCallback(any(NetworkCallback.class));
+        verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class));
 
         verifyNoMoreInteractions(mCM);
     }
@@ -237,7 +237,7 @@
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
 
-        mUNM.start(mDefaultRequest);
+        mUNM.startObserveAllNetworks();
         assertFalse(mUNM.mobileNetworkRequested());
         assertEquals(0, mCM.requested.size());
 
@@ -257,7 +257,7 @@
 
     @Test
     public void testUpdateMobileRequiresDun() throws Exception {
-        mUNM.start(mDefaultRequest);
+        mUNM.startObserveAllNetworks();
 
         // Test going from no-DUN to DUN correctly re-registers callbacks.
         mUNM.updateMobileRequiresDun(false);
@@ -285,7 +285,8 @@
         final Collection<Integer> preferredTypes = new ArrayList<>();
         preferredTypes.add(TYPE_WIFI);
 
-        mUNM.start(mDefaultRequest);
+        mUNM.startTrackDefaultNetwork(mDefaultRequest);
+        mUNM.startObserveAllNetworks();
         // There are no networks, so there is nothing to select.
         assertSatisfiesLegacyType(TYPE_NONE, mUNM.selectPreferredUpstreamType(preferredTypes));
 
@@ -350,7 +351,8 @@
 
     @Test
     public void testGetCurrentPreferredUpstream() throws Exception {
-        mUNM.start(mDefaultRequest);
+        mUNM.startTrackDefaultNetwork(mDefaultRequest);
+        mUNM.startObserveAllNetworks();
         mUNM.updateMobileRequiresDun(false);
 
         // [0] Mobile connects, DUN not required -> mobile selected.
@@ -389,7 +391,8 @@
 
     @Test
     public void testLocalPrefixes() throws Exception {
-        mUNM.start(mDefaultRequest);
+        mUNM.startTrackDefaultNetwork(mDefaultRequest);
+        mUNM.startObserveAllNetworks();
 
         // [0] Test minimum set of local prefixes.
         Set<IpPrefix> local = mUNM.getLocalPrefixes();
@@ -521,11 +524,19 @@
         }
 
         boolean hasNoCallbacks() {
-            return allCallbacks.isEmpty() &&
-                   trackingDefault.isEmpty() &&
-                   listening.isEmpty() &&
-                   requested.isEmpty() &&
-                   legacyTypeMap.isEmpty();
+            return allCallbacks.isEmpty()
+                    && trackingDefault.isEmpty()
+                    && listening.isEmpty()
+                    && requested.isEmpty()
+                    && legacyTypeMap.isEmpty();
+        }
+
+        boolean onlyHasDefaultCallbacks() {
+            return (allCallbacks.size() == 1)
+                    && (trackingDefault.size() == 1)
+                    && listening.isEmpty()
+                    && requested.isEmpty()
+                    && legacyTypeMap.isEmpty();
         }
 
         boolean isListeningForAll() {
diff --git a/tests/net/jni/apf_jni.cpp b/tests/net/jni/apf_jni.cpp
index 1ea9e27..4222adf 100644
--- a/tests/net/jni/apf_jni.cpp
+++ b/tests/net/jni/apf_jni.cpp
@@ -21,37 +21,40 @@
 #include <stdlib.h>
 #include <string>
 #include <utils/Log.h>
+#include <vector>
 
 #include "apf_interpreter.h"
+#include "nativehelper/scoped_primitive_array.h"
 
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 
 // JNI function acting as simply call-through to native APF interpreter.
 static jint com_android_server_ApfTest_apfSimulate(
-        JNIEnv* env, jclass, jbyteArray program, jbyteArray packet,
-        jbyteArray data, jint filter_age) {
-    uint8_t* program_raw = (uint8_t*)env->GetByteArrayElements(program, nullptr);
-    uint8_t* packet_raw = (uint8_t*)env->GetByteArrayElements(packet, nullptr);
-    uint8_t* data_raw = (uint8_t*)(data ? env->GetByteArrayElements(data, nullptr) : nullptr);
-    uint32_t program_len = env->GetArrayLength(program);
-    uint32_t packet_len = env->GetArrayLength(packet);
-    uint32_t data_len = data ? env->GetArrayLength(data) : 0;
+        JNIEnv* env, jclass, jbyteArray jprogram, jbyteArray jpacket,
+        jbyteArray jdata, jint filter_age) {
 
-    // Merge program and data into a single buffer.
-    uint8_t* program_and_data = (uint8_t*)malloc(program_len + data_len);
-    memcpy(program_and_data, program_raw, program_len);
-    memcpy(program_and_data + program_len, data_raw, data_len);
+    ScopedByteArrayRO packet(env, jpacket);
+    uint32_t packet_len = (uint32_t)packet.size();
+    uint32_t program_len = env->GetArrayLength(jprogram);
+    uint32_t data_len = jdata ? env->GetArrayLength(jdata) : 0;
+    std::vector<uint8_t> buf(program_len + data_len, 0);
+
+    env->GetByteArrayRegion(jprogram, 0, program_len, reinterpret_cast<jbyte*>(buf.data()));
+    if (jdata) {
+        // Merge program and data into a single buffer.
+        env->GetByteArrayRegion(jdata, 0, data_len,
+                                reinterpret_cast<jbyte*>(buf.data() + program_len));
+    }
 
     jint result =
-        accept_packet(program_and_data, program_len, program_len + data_len,
-                      packet_raw, packet_len, filter_age);
-    if (data) {
-        memcpy(data_raw, program_and_data + program_len, data_len);
-        env->ReleaseByteArrayElements(data, (jbyte*)data_raw, 0 /* copy back */);
+        accept_packet(buf.data(), program_len, program_len + data_len,
+                        reinterpret_cast<const uint8_t*>(packet.get()), packet_len, filter_age);
+
+    if (jdata) {
+        env->SetByteArrayRegion(jdata, 0, data_len,
+                                reinterpret_cast<jbyte*>(buf.data() + program_len));
     }
-    free(program_and_data);
-    env->ReleaseByteArrayElements(packet, (jbyte*)packet_raw, JNI_ABORT);
-    env->ReleaseByteArrayElements(program, (jbyte*)program_raw, JNI_ABORT);
+
     return result;
 }
 
@@ -118,8 +121,7 @@
         jstring jpcap_filename, jbyteArray japf_program) {
     ScopedUtfChars filter(env, jfilter);
     ScopedUtfChars pcap_filename(env, jpcap_filename);
-    uint8_t* apf_program = (uint8_t*)env->GetByteArrayElements(japf_program, NULL);
-    uint32_t apf_program_len = env->GetArrayLength(japf_program);
+    ScopedByteArrayRO apf_program(env, japf_program);
 
     // Open pcap file for BPF filtering
     ScopedFILE bpf_fp(fopen(pcap_filename.c_str(), "rb"));
@@ -161,14 +163,15 @@
         do {
             apf_packet = pcap_next(apf_pcap.get(), &apf_header);
         } while (apf_packet != NULL && !accept_packet(
-                apf_program, apf_program_len, 0 /* data_len */,
+                reinterpret_cast<uint8_t*>(const_cast<int8_t*>(apf_program.get())),
+                apf_program.size(), 0 /* data_len */,
                 apf_packet, apf_header.len, 0 /* filter_age */));
 
         // Make sure both filters matched the same packet.
         if (apf_packet == NULL && bpf_packet == NULL)
-             break;
+            break;
         if (apf_packet == NULL || bpf_packet == NULL)
-             return false;
+            return false;
         if (apf_header.len != bpf_header.len ||
                 apf_header.ts.tv_sec != bpf_header.ts.tv_sec ||
                 apf_header.ts.tv_usec != bpf_header.ts.tv_usec ||
@@ -178,6 +181,48 @@
     return true;
 }
 
+static jboolean com_android_server_ApfTest_dropsAllPackets(JNIEnv* env, jclass, jbyteArray jprogram,
+        jbyteArray jdata, jstring jpcap_filename) {
+    ScopedUtfChars pcap_filename(env, jpcap_filename);
+    ScopedByteArrayRO apf_program(env, jprogram);
+    uint32_t apf_program_len = (uint32_t)apf_program.size();
+    uint32_t data_len = env->GetArrayLength(jdata);
+    pcap_pkthdr apf_header;
+    const uint8_t* apf_packet;
+    char pcap_error[PCAP_ERRBUF_SIZE];
+    std::vector<uint8_t> buf(apf_program_len + data_len, 0);
+
+    // Merge program and data into a single buffer.
+    env->GetByteArrayRegion(jprogram, 0, apf_program_len, reinterpret_cast<jbyte*>(buf.data()));
+    env->GetByteArrayRegion(jdata, 0, data_len,
+                            reinterpret_cast<jbyte*>(buf.data() + apf_program_len));
+
+    // Open pcap file
+    ScopedFILE apf_fp(fopen(pcap_filename.c_str(), "rb"));
+    ScopedPcap apf_pcap(pcap_fopen_offline(apf_fp.get(), pcap_error));
+
+    if (apf_pcap.get() == NULL) {
+        throwException(env, "pcap_fopen_offline failed: " + std::string(pcap_error));
+        return false;
+    }
+
+    while ((apf_packet = pcap_next(apf_pcap.get(), &apf_header)) != NULL) {
+        int result = accept_packet(buf.data(), apf_program_len,
+                                    apf_program_len + data_len, apf_packet, apf_header.len, 0);
+
+        // Return false once packet passes the filter
+        if (result) {
+            env->SetByteArrayRegion(jdata, 0, data_len,
+                                    reinterpret_cast<jbyte*>(buf.data() + apf_program_len));
+            return false;
+         }
+    }
+
+    env->SetByteArrayRegion(jdata, 0, data_len,
+                            reinterpret_cast<jbyte*>(buf.data() + apf_program_len));
+    return true;
+}
+
 extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
     JNIEnv *env;
     if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
@@ -192,6 +237,8 @@
                     (void*)com_android_server_ApfTest_compileToBpf },
             { "compareBpfApf", "(Ljava/lang/String;Ljava/lang/String;[B)Z",
                     (void*)com_android_server_ApfTest_compareBpfApf },
+            { "dropsAllPackets", "([B[BLjava/lang/String;)Z",
+                    (void*)com_android_server_ApfTest_dropsAllPackets },
     };
 
     jniRegisterNativeMethods(env, "android/net/apf/ApfTest",
diff --git a/tests/net/res/raw/apfPcap.pcap b/tests/net/res/raw/apfPcap.pcap
new file mode 100644
index 0000000..6f69c4a
--- /dev/null
+++ b/tests/net/res/raw/apfPcap.pcap
Binary files differ
diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py
index cb8fef9..b5a990e 100644
--- a/tools/apilint/apilint.py
+++ b/tools/apilint/apilint.py
@@ -183,6 +183,11 @@
 
         self.name = self.fullname[self.fullname.rindex(".")+1:]
 
+    def merge_from(self, other):
+        self.ctors.extend(other.ctors)
+        self.fields.extend(other.fields)
+        self.methods.extend(other.methods)
+
     def __hash__(self):
         return hash((self.raw, tuple(self.ctors), tuple(self.fields), tuple(self.methods)))
 
@@ -204,9 +209,28 @@
         return self.raw
 
 
-def _parse_stream(f, clazz_cb=None):
-    line = 0
+def _parse_stream(f, clazz_cb=None, base_f=None):
     api = {}
+
+    if base_f:
+        base_classes = _parse_stream_to_generator(base_f)
+    else:
+        base_classes = []
+
+    for clazz in _parse_stream_to_generator(f):
+        base_class = _parse_to_matching_class(base_classes, clazz)
+        if base_class:
+            clazz.merge_from(base_class)
+
+        if clazz_cb:
+            clazz_cb(clazz)
+        else: # In callback mode, don't keep track of the full API
+            api[clazz.fullname] = clazz
+
+    return api
+
+def _parse_stream_to_generator(f):
+    line = 0
     pkg = None
     clazz = None
     blame = None
@@ -225,26 +249,41 @@
         if raw.startswith("package"):
             pkg = Package(line, raw, blame)
         elif raw.startswith("  ") and raw.endswith("{"):
-            # When provided with class callback, we treat as incremental
-            # parse and don't build up entire API
-            if clazz and clazz_cb:
-                clazz_cb(clazz)
             clazz = Class(pkg, line, raw, blame)
-            if not clazz_cb:
-                api[clazz.fullname] = clazz
         elif raw.startswith("    ctor"):
             clazz.ctors.append(Method(clazz, line, raw, blame))
         elif raw.startswith("    method"):
             clazz.methods.append(Method(clazz, line, raw, blame))
         elif raw.startswith("    field"):
             clazz.fields.append(Field(clazz, line, raw, blame))
+        elif raw.startswith("  }") and clazz:
+            while True:
+                retry = yield clazz
+                if not retry:
+                    break
+                # send() was called, asking us to redeliver clazz on next(). Still need to yield
+                # a dummy value to the send() first though.
+                if (yield "Returning clazz on next()"):
+                        raise TypeError("send() must be followed by next(), not send()")
 
-    # Handle last trailing class
-    if clazz and clazz_cb:
-        clazz_cb(clazz)
 
-    return api
+def _parse_to_matching_class(classes, needle):
+    """Takes a classes generator and parses it until it returns the class we're looking for
 
+    This relies on classes being sorted by package and class name."""
+
+    for clazz in classes:
+        if clazz.pkg.name < needle.pkg.name:
+            # We haven't reached the right package yet
+            continue
+        if clazz.name < needle.name:
+            # We haven't reached the right class yet
+            continue
+        if clazz.fullname == needle.fullname:
+            return clazz
+        # We ran past the right class. Send it back into the generator, then report failure.
+        classes.send(clazz)
+        return None
 
 class Failure():
     def __init__(self, sig, clazz, detail, error, rule, msg):
@@ -1504,12 +1543,12 @@
     verify_singleton(clazz)
 
 
-def examine_stream(stream):
+def examine_stream(stream, base_stream=None):
     """Find all style issues in the given API stream."""
     global failures, noticed
     failures = {}
     noticed = {}
-    _parse_stream(stream, examine_clazz)
+    _parse_stream(stream, examine_clazz, base_f=base_stream)
     return (failures, noticed)
 
 
@@ -1650,6 +1689,12 @@
     parser.add_argument("current.txt", type=argparse.FileType('r'), help="current.txt")
     parser.add_argument("previous.txt", nargs='?', type=argparse.FileType('r'), default=None,
             help="previous.txt")
+    parser.add_argument("--base-current", nargs='?', type=argparse.FileType('r'), default=None,
+            help="The base current.txt to use when examining system-current.txt or"
+                 " test-current.txt")
+    parser.add_argument("--base-previous", nargs='?', type=argparse.FileType('r'), default=None,
+            help="The base previous.txt to use when examining system-previous.txt or"
+                 " test-previous.txt")
     parser.add_argument("--no-color", action='store_const', const=True,
             help="Disable terminal colors")
     parser.add_argument("--allow-google", action='store_const', const=True,
@@ -1669,7 +1714,9 @@
         ALLOW_GOOGLE = True
 
     current_file = args['current.txt']
+    base_current_file = args['base_current']
     previous_file = args['previous.txt']
+    base_previous_file = args['base_previous']
 
     if args['show_deprecations_at_birth']:
         with current_file as f:
@@ -1688,10 +1735,18 @@
         sys.exit()
 
     with current_file as f:
-        cur_fail, cur_noticed = examine_stream(f)
+        if base_current_file:
+            with base_current_file as base_f:
+                cur_fail, cur_noticed = examine_stream(f, base_f)
+        else:
+            cur_fail, cur_noticed = examine_stream(f)
     if not previous_file is None:
         with previous_file as f:
-            prev_fail, prev_noticed = examine_stream(f)
+            if base_previous_file:
+                with base_previous_file as base_f:
+                    prev_fail, prev_noticed = examine_stream(f, base_f)
+            else:
+                prev_fail, prev_noticed = examine_stream(f)
 
         # ignore errors from previous API level
         for p in prev_fail: