Merge "Remove recents button and longpress home when connected to service (1/2)"
diff --git a/Android.bp b/Android.bp
index 015e59f..979ed1c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -463,6 +463,17 @@
         "telecomm/java/com/android/internal/telecom/IInCallService.aidl",
         "telecomm/java/com/android/internal/telecom/ITelecomService.aidl",
         "telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl",
+        "telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl",
+        "telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl",
+        "telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl",
+        "telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl",
+        "telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl",
+        "telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl",
+        "telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl",
+        "telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl",
+        "telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl",
+        "telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl",
+        "telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl",
         "telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl",
         "telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl",
         "telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl",
@@ -553,7 +564,7 @@
     ],
 
     aidl: {
-        local_include_dirs: [
+        export_include_dirs: [
             // From build/make/core/pathmap.mk FRAMEWORK_BASE_SUBDIRS
             "core/java",
             "graphics/java",
@@ -624,7 +635,7 @@
     name: "framework-statslog-gen",
     tools: ["stats-log-api-gen"],
     cmd: "$(location stats-log-api-gen) --java $(out)",
-    out: ["android/util/StatsLog.java"],
+    out: ["android/util/StatsLogInternal.java"],
 }
 
 gensrcs {
diff --git a/Android.mk b/Android.mk
index d4a1dcf..1035362 100644
--- a/Android.mk
+++ b/Android.mk
@@ -278,7 +278,7 @@
   $(call all-java-files-under, test-mock/src/android/test/mock)
 
 framework_base_android_test_runner_src_files := \
-  $(call all-java-files-under, test-runner/src)
+  $(call all-java-files-under, test-runner/src/junit)
 
 # Find all files in specific directories (relative to frameworks/base)
 # to document and check apis
diff --git a/api/current.txt b/api/current.txt
index a01de4f..1bc38d4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6418,6 +6418,7 @@
     method public boolean isResetPasswordTokenActive(android.content.ComponentName);
     method public boolean isSecurityLoggingEnabled(android.content.ComponentName);
     method public boolean isUninstallBlocked(android.content.ComponentName, java.lang.String);
+    method public boolean isUsingUnifiedPassword(android.content.ComponentName);
     method public void lockNow();
     method public void lockNow(int);
     method public boolean logoutUser(android.content.ComponentName);
@@ -7019,6 +7020,7 @@
     method public android.net.Uri getUri();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.app.slice.Slice> CREATOR;
+    field public static final java.lang.String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
     field public static final java.lang.String HINT_ACTIONS = "actions";
     field public static final java.lang.String HINT_HORIZONTAL = "horizontal";
     field public static final java.lang.String HINT_LARGE = "large";
@@ -7028,11 +7030,13 @@
     field public static final java.lang.String HINT_NO_TINT = "no_tint";
     field public static final java.lang.String HINT_PARTIAL = "partial";
     field public static final java.lang.String HINT_SELECTED = "selected";
+    field public static final java.lang.String HINT_SUMMARY = "summary";
     field public static final java.lang.String HINT_TITLE = "title";
     field public static final java.lang.String SUBTYPE_COLOR = "color";
     field public static final java.lang.String SUBTYPE_MESSAGE = "message";
     field public static final java.lang.String SUBTYPE_SLIDER = "slider";
     field public static final java.lang.String SUBTYPE_SOURCE = "source";
+    field public static final java.lang.String SUBTYPE_TOGGLE = "toggle";
   }
 
   public static class Slice.Builder {
@@ -7090,6 +7094,20 @@
     field public static final java.lang.String FORMAT_TIMESTAMP = "timestamp";
   }
 
+  public class SliceManager {
+    method public java.util.List<android.app.slice.SliceSpec> getPinnedSpecs(android.net.Uri);
+    method public void pinSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
+    method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>);
+    method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, android.os.Handler);
+    method public void registerSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback, java.util.List<android.app.slice.SliceSpec>, java.util.concurrent.Executor);
+    method public void unpinSlice(android.net.Uri);
+    method public void unregisterSliceCallback(android.net.Uri, android.app.slice.SliceManager.SliceCallback);
+  }
+
+  public static abstract interface SliceManager.SliceCallback {
+    method public abstract void onSliceUpdated(android.app.slice.Slice);
+  }
+
   public abstract class SliceProvider extends android.content.ContentProvider {
     ctor public SliceProvider();
     method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]);
@@ -7098,6 +7116,8 @@
     method public android.app.slice.Slice onBindSlice(android.net.Uri, java.util.List<android.app.slice.SliceSpec>);
     method public deprecated android.app.slice.Slice onBindSlice(android.net.Uri);
     method public android.net.Uri onMapIntentToUri(android.content.Intent);
+    method public void onSlicePinned(android.net.Uri);
+    method public void onSliceUnpinned(android.net.Uri);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, android.os.CancellationSignal);
     method public final android.database.Cursor query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal);
@@ -8119,6 +8139,94 @@
     method public void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int);
   }
 
+  public final class BluetoothHidDevice implements android.bluetooth.BluetoothProfile {
+    method public boolean connect(android.bluetooth.BluetoothDevice);
+    method public boolean disconnect(android.bluetooth.BluetoothDevice);
+    method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+    method public int getConnectionState(android.bluetooth.BluetoothDevice);
+    method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
+    method public boolean registerApp(android.bluetooth.BluetoothHidDeviceAppSdpSettings, android.bluetooth.BluetoothHidDeviceAppQosSettings, android.bluetooth.BluetoothHidDeviceAppQosSettings, android.bluetooth.BluetoothHidDeviceCallback);
+    method public boolean replyReport(android.bluetooth.BluetoothDevice, byte, byte, byte[]);
+    method public boolean reportError(android.bluetooth.BluetoothDevice, byte);
+    method public boolean sendReport(android.bluetooth.BluetoothDevice, int, byte[]);
+    method public boolean unregisterApp();
+    field public static final java.lang.String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED";
+    field public static final byte ERROR_RSP_INVALID_PARAM = 4; // 0x4
+    field public static final byte ERROR_RSP_INVALID_RPT_ID = 2; // 0x2
+    field public static final byte ERROR_RSP_NOT_READY = 1; // 0x1
+    field public static final byte ERROR_RSP_SUCCESS = 0; // 0x0
+    field public static final byte ERROR_RSP_UNKNOWN = 14; // 0xe
+    field public static final byte ERROR_RSP_UNSUPPORTED_REQ = 3; // 0x3
+    field public static final byte PROTOCOL_BOOT_MODE = 0; // 0x0
+    field public static final byte PROTOCOL_REPORT_MODE = 1; // 0x1
+    field public static final byte REPORT_TYPE_FEATURE = 3; // 0x3
+    field public static final byte REPORT_TYPE_INPUT = 1; // 0x1
+    field public static final byte REPORT_TYPE_OUTPUT = 2; // 0x2
+    field public static final byte SUBCLASS1_COMBO = -64; // 0xffffffc0
+    field public static final byte SUBCLASS1_KEYBOARD = 64; // 0x40
+    field public static final byte SUBCLASS1_MOUSE = -128; // 0xffffff80
+    field public static final byte SUBCLASS1_NONE = 0; // 0x0
+    field public static final byte SUBCLASS2_CARD_READER = 6; // 0x6
+    field public static final byte SUBCLASS2_DIGITIZER_TABLET = 5; // 0x5
+    field public static final byte SUBCLASS2_GAMEPAD = 2; // 0x2
+    field public static final byte SUBCLASS2_JOYSTICK = 1; // 0x1
+    field public static final byte SUBCLASS2_REMOTE_CONTROL = 3; // 0x3
+    field public static final byte SUBCLASS2_SENSING_DEVICE = 4; // 0x4
+    field public static final byte SUBCLASS2_UNCATEGORIZED = 0; // 0x0
+  }
+
+  public final class BluetoothHidDeviceAppQosSettings implements android.os.Parcelable {
+    ctor public BluetoothHidDeviceAppQosSettings(int, int, int, int, int, int);
+    method public int describeContents();
+    method public int[] toArray();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHidDeviceAppQosSettings> CREATOR;
+    field public static final int MAX = -1; // 0xffffffff
+    field public static final int SERVICE_BEST_EFFORT = 1; // 0x1
+    field public static final int SERVICE_GUARANTEED = 2; // 0x2
+    field public static final int SERVICE_NO_TRAFFIC = 0; // 0x0
+    field public final int delayVariation;
+    field public final int latency;
+    field public final int peakBandwidth;
+    field public final int serviceType;
+    field public final int tokenBucketSize;
+    field public final int tokenRate;
+  }
+
+  public static class BluetoothHidDeviceAppQosSettings.Builder {
+    ctor public BluetoothHidDeviceAppQosSettings.Builder();
+    method public android.bluetooth.BluetoothHidDeviceAppQosSettings build();
+    method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder delayVariation(int);
+    method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder latency(int);
+    method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder peakBandwidth(int);
+    method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder serviceType(int);
+    method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder tokenBucketSize(int);
+    method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder tokenRate(int);
+  }
+
+  public final class BluetoothHidDeviceAppSdpSettings implements android.os.Parcelable {
+    ctor public BluetoothHidDeviceAppSdpSettings(java.lang.String, java.lang.String, java.lang.String, byte, byte[]);
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHidDeviceAppSdpSettings> CREATOR;
+    field public final java.lang.String description;
+    field public final byte[] descriptors;
+    field public final java.lang.String name;
+    field public final java.lang.String provider;
+    field public final byte subclass;
+  }
+
+  public abstract class BluetoothHidDeviceCallback {
+    ctor public BluetoothHidDeviceCallback();
+    method public void onAppStatusChanged(android.bluetooth.BluetoothDevice, boolean);
+    method public void onConnectionStateChanged(android.bluetooth.BluetoothDevice, int);
+    method public void onGetReport(android.bluetooth.BluetoothDevice, byte, byte, int);
+    method public void onIntrData(android.bluetooth.BluetoothDevice, byte, byte[]);
+    method public void onSetProtocol(android.bluetooth.BluetoothDevice, byte);
+    method public void onSetReport(android.bluetooth.BluetoothDevice, byte, byte, byte[]);
+    method public void onVirtualCableUnplug(android.bluetooth.BluetoothDevice);
+  }
+
   public final class BluetoothManager {
     method public android.bluetooth.BluetoothAdapter getAdapter();
     method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int);
@@ -8138,6 +8246,7 @@
     field public static final int GATT_SERVER = 8; // 0x8
     field public static final int HEADSET = 1; // 0x1
     field public static final int HEALTH = 3; // 0x3
+    field public static final int HID_DEVICE = 19; // 0x13
     field public static final int SAP = 10; // 0xa
     field public static final int STATE_CONNECTED = 2; // 0x2
     field public static final int STATE_CONNECTING = 1; // 0x1
@@ -25919,12 +26028,12 @@
   }
 
   public final class IpSecManager {
+    method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
+    method public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
     method public void applyTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
     method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
     method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
     method public void removeTransportModeTransform(java.io.FileDescriptor, android.net.IpSecTransform) throws java.io.IOException;
-    method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
-    method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(int, java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
   }
 
   public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
@@ -32123,6 +32232,7 @@
     field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location";
     field public static final java.lang.String DISALLOW_SMS = "no_sms";
     field public static final java.lang.String DISALLOW_SYSTEM_ERROR_DIALOGS = "no_system_error_dialogs";
+    field public static final java.lang.String DISALLOW_UNIFIED_PASSWORD = "no_unified_password";
     field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
     field public static final java.lang.String DISALLOW_UNMUTE_MICROPHONE = "no_unmute_microphone";
     field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
@@ -37512,6 +37622,25 @@
     method public android.service.autofill.Dataset.Builder setValue(android.view.autofill.AutofillId, android.view.autofill.AutofillValue, java.util.regex.Pattern, android.widget.RemoteViews);
   }
 
+  public final class EditDistanceScorer implements android.os.Parcelable android.service.autofill.Scorer {
+    method public int describeContents();
+    method public static android.service.autofill.EditDistanceScorer getInstance();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.EditDistanceScorer> CREATOR;
+  }
+
+  public final class FieldClassification implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.util.List<android.service.autofill.FieldClassification.Match> getMatches();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.FieldClassification> CREATOR;
+  }
+
+  public static final class FieldClassification.Match {
+    method public java.lang.String getRemoteId();
+    method public float getScore();
+  }
+
   public final class FillCallback {
     method public void onFailure(java.lang.CharSequence);
     method public void onSuccess(android.service.autofill.FillResponse);
@@ -37537,6 +37666,7 @@
     method public java.util.Map<android.view.autofill.AutofillId, java.lang.String> getChangedFields();
     method public android.os.Bundle getClientState();
     method public java.lang.String getDatasetId();
+    method public java.util.Map<android.view.autofill.AutofillId, android.service.autofill.FieldClassification> getFieldsClassification();
     method public java.util.Set<java.lang.String> getIgnoredDatasetIds();
     method public java.util.Map<android.view.autofill.AutofillId, java.util.Set<java.lang.String>> getManuallyEnteredField();
     method public java.util.Set<java.lang.String> getSelectedDatasetIds();
@@ -37574,6 +37704,7 @@
     method public android.service.autofill.FillResponse.Builder disableAutofill(long);
     method public android.service.autofill.FillResponse.Builder setAuthentication(android.view.autofill.AutofillId[], android.content.IntentSender, android.widget.RemoteViews);
     method public android.service.autofill.FillResponse.Builder setClientState(android.os.Bundle);
+    method public android.service.autofill.FillResponse.Builder setFieldClassificationIds(android.view.autofill.AutofillId...);
     method public android.service.autofill.FillResponse.Builder setFlags(int);
     method public android.service.autofill.FillResponse.Builder setFooter(android.widget.RemoteViews);
     method public android.service.autofill.FillResponse.Builder setHeader(android.widget.RemoteViews);
@@ -37657,6 +37788,9 @@
     field public static final android.os.Parcelable.Creator<android.service.autofill.SaveRequest> CREATOR;
   }
 
+  public abstract interface Scorer {
+  }
+
   public final class TextValueSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
     ctor public TextValueSanitizer(java.util.regex.Pattern, java.lang.String);
     method public int describeContents();
@@ -37667,6 +37801,22 @@
   public abstract interface Transformation {
   }
 
+  public final class UserData implements android.os.Parcelable {
+    method public int describeContents();
+    method public static int getMaxFieldClassificationIdsSize();
+    method public static int getMaxUserDataSize();
+    method public static int getMaxValueLength();
+    method public static int getMinValueLength();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.autofill.UserData> CREATOR;
+  }
+
+  public static final class UserData.Builder {
+    ctor public UserData.Builder(android.service.autofill.Scorer, java.lang.String, java.lang.String);
+    method public android.service.autofill.UserData.Builder add(java.lang.String, java.lang.String);
+    method public android.service.autofill.UserData build();
+  }
+
   public abstract interface Validator {
   }
 
@@ -41300,41 +41450,6 @@
 
 package android.test {
 
-  public abstract deprecated class ActivityInstrumentationTestCase<T extends android.app.Activity> extends android.test.ActivityTestCase {
-    ctor public ActivityInstrumentationTestCase(java.lang.String, java.lang.Class<T>);
-    ctor public ActivityInstrumentationTestCase(java.lang.String, java.lang.Class<T>, boolean);
-    method public T getActivity();
-    method public void testActivityTestCaseSetUpProperly() throws java.lang.Exception;
-  }
-
-  public abstract deprecated class ActivityInstrumentationTestCase2<T extends android.app.Activity> extends android.test.ActivityTestCase {
-    ctor public deprecated ActivityInstrumentationTestCase2(java.lang.String, java.lang.Class<T>);
-    ctor public ActivityInstrumentationTestCase2(java.lang.Class<T>);
-    method public T getActivity();
-    method public void setActivityInitialTouchMode(boolean);
-    method public void setActivityIntent(android.content.Intent);
-  }
-
-  public abstract deprecated class ActivityTestCase extends android.test.InstrumentationTestCase {
-    ctor public ActivityTestCase();
-    method protected android.app.Activity getActivity();
-    method protected void scrubClass(java.lang.Class<?>) throws java.lang.IllegalAccessException;
-    method protected void setActivity(android.app.Activity);
-  }
-
-  public abstract deprecated class ActivityUnitTestCase<T extends android.app.Activity> extends android.test.ActivityTestCase {
-    ctor public ActivityUnitTestCase(java.lang.Class<T>);
-    method public T getActivity();
-    method public int getFinishedActivityRequest();
-    method public int getRequestedOrientation();
-    method public android.content.Intent getStartedActivityIntent();
-    method public int getStartedActivityRequest();
-    method public boolean isFinishCalled();
-    method public void setActivityContext(android.content.Context);
-    method public void setApplication(android.app.Application);
-    method protected T startActivity(android.content.Intent, android.os.Bundle, java.lang.Object);
-  }
-
   public deprecated class AndroidTestCase extends junit.framework.TestCase {
     ctor public AndroidTestCase();
     method public void assertActivityRequiresPermission(java.lang.String, java.lang.String, java.lang.String);
@@ -41347,46 +41462,6 @@
     field protected android.content.Context mContext;
   }
 
-  public deprecated class AndroidTestRunner extends junit.runner.BaseTestRunner {
-    ctor public AndroidTestRunner();
-    method public void addTestListener(junit.framework.TestListener);
-    method public void clearTestListeners();
-    method protected junit.framework.TestResult createTestResult();
-    method public java.util.List<junit.framework.TestCase> getTestCases();
-    method public java.lang.String getTestClassName();
-    method public junit.framework.TestResult getTestResult();
-    method protected java.lang.Class loadSuiteClass(java.lang.String) throws java.lang.ClassNotFoundException;
-    method protected void runFailed(java.lang.String);
-    method public void runTest();
-    method public void runTest(junit.framework.TestResult);
-    method public void setContext(android.content.Context);
-    method public deprecated void setInstrumentaiton(android.app.Instrumentation);
-    method public void setInstrumentation(android.app.Instrumentation);
-    method public void setTest(junit.framework.Test);
-    method public void setTestClassName(java.lang.String, java.lang.String);
-    method public void testEnded(java.lang.String);
-    method public void testFailed(int, junit.framework.Test, java.lang.Throwable);
-    method public void testStarted(java.lang.String);
-  }
-
-  public abstract deprecated class ApplicationTestCase<T extends android.app.Application> extends android.test.AndroidTestCase {
-    ctor public ApplicationTestCase(java.lang.Class<T>);
-    method protected final void createApplication();
-    method public T getApplication();
-    method public android.content.Context getSystemContext();
-    method protected final void terminateApplication();
-    method public final void testApplicationTestCaseSetUpProperly() throws java.lang.Exception;
-  }
-
-  public deprecated class AssertionFailedError extends java.lang.Error {
-    ctor public AssertionFailedError();
-    ctor public AssertionFailedError(java.lang.String);
-  }
-
-  public deprecated class ComparisonFailure extends android.test.AssertionFailedError {
-    ctor public ComparisonFailure(java.lang.String, java.lang.String, java.lang.String);
-  }
-
   public abstract deprecated class FlakyTest implements java.lang.annotation.Annotation {
   }
 
@@ -41403,25 +41478,6 @@
     method public void sendRepeatedKeys(int...);
   }
 
-  public deprecated class InstrumentationTestRunner extends android.app.Instrumentation implements android.test.TestSuiteProvider {
-    ctor public InstrumentationTestRunner();
-    method public junit.framework.TestSuite getAllTests();
-    method protected android.test.AndroidTestRunner getAndroidTestRunner();
-    method public android.os.Bundle getArguments();
-    method public java.lang.ClassLoader getLoader();
-    method public junit.framework.TestSuite getTestSuite();
-    field public static final java.lang.String REPORT_KEY_NAME_CLASS = "class";
-    field public static final java.lang.String REPORT_KEY_NAME_TEST = "test";
-    field public static final java.lang.String REPORT_KEY_NUM_CURRENT = "current";
-    field public static final java.lang.String REPORT_KEY_NUM_TOTAL = "numtests";
-    field public static final java.lang.String REPORT_KEY_STACK = "stack";
-    field public static final java.lang.String REPORT_VALUE_ID = "InstrumentationTestRunner";
-    field public static final int REPORT_VALUE_RESULT_ERROR = -1; // 0xffffffff
-    field public static final int REPORT_VALUE_RESULT_FAILURE = -2; // 0xfffffffe
-    field public static final int REPORT_VALUE_RESULT_OK = 0; // 0x0
-    field public static final int REPORT_VALUE_RESULT_START = 1; // 0x1
-  }
-
   public deprecated class InstrumentationTestSuite extends junit.framework.TestSuite {
     ctor public InstrumentationTestSuite(android.app.Instrumentation);
     ctor public InstrumentationTestSuite(java.lang.String, android.app.Instrumentation);
@@ -41429,55 +41485,6 @@
     method public void addTestSuite(java.lang.Class);
   }
 
-  public deprecated class IsolatedContext extends android.content.ContextWrapper {
-    ctor public IsolatedContext(android.content.ContentResolver, android.content.Context);
-    method public java.util.List<android.content.Intent> getAndClearBroadcastIntents();
-  }
-
-  public class LoaderTestCase extends android.test.AndroidTestCase {
-    ctor public LoaderTestCase();
-    method public <T> T getLoaderResultSynchronously(android.content.Loader<T>);
-  }
-
-  public final deprecated class MoreAsserts {
-    method public static void assertAssignableFrom(java.lang.Class<?>, java.lang.Object);
-    method public static void assertAssignableFrom(java.lang.Class<?>, java.lang.Class<?>);
-    method public static java.util.regex.MatchResult assertContainsRegex(java.lang.String, java.lang.String, java.lang.String);
-    method public static java.util.regex.MatchResult assertContainsRegex(java.lang.String, java.lang.String);
-    method public static void assertContentsInAnyOrder(java.lang.String, java.lang.Iterable<?>, java.lang.Object...);
-    method public static void assertContentsInAnyOrder(java.lang.Iterable<?>, java.lang.Object...);
-    method public static void assertContentsInOrder(java.lang.String, java.lang.Iterable<?>, java.lang.Object...);
-    method public static void assertContentsInOrder(java.lang.Iterable<?>, java.lang.Object...);
-    method public static void assertEmpty(java.lang.String, java.lang.Iterable<?>);
-    method public static void assertEmpty(java.lang.Iterable<?>);
-    method public static void assertEmpty(java.lang.String, java.util.Map<?, ?>);
-    method public static void assertEmpty(java.util.Map<?, ?>);
-    method public static void assertEquals(java.lang.String, byte[], byte[]);
-    method public static void assertEquals(byte[], byte[]);
-    method public static void assertEquals(java.lang.String, int[], int[]);
-    method public static void assertEquals(int[], int[]);
-    method public static void assertEquals(java.lang.String, double[], double[]);
-    method public static void assertEquals(double[], double[]);
-    method public static void assertEquals(java.lang.String, java.lang.Object[], java.lang.Object[]);
-    method public static void assertEquals(java.lang.Object[], java.lang.Object[]);
-    method public static void assertEquals(java.lang.String, java.util.Set<? extends java.lang.Object>, java.util.Set<? extends java.lang.Object>);
-    method public static void assertEquals(java.util.Set<? extends java.lang.Object>, java.util.Set<? extends java.lang.Object>);
-    method public static java.util.regex.MatchResult assertMatchesRegex(java.lang.String, java.lang.String, java.lang.String);
-    method public static java.util.regex.MatchResult assertMatchesRegex(java.lang.String, java.lang.String);
-    method public static void assertNotContainsRegex(java.lang.String, java.lang.String, java.lang.String);
-    method public static void assertNotContainsRegex(java.lang.String, java.lang.String);
-    method public static void assertNotEmpty(java.lang.String, java.lang.Iterable<?>);
-    method public static void assertNotEmpty(java.lang.Iterable<?>);
-    method public static void assertNotEmpty(java.lang.String, java.util.Map<?, ?>);
-    method public static void assertNotEmpty(java.util.Map<?, ?>);
-    method public static void assertNotEqual(java.lang.String, java.lang.Object, java.lang.Object);
-    method public static void assertNotEqual(java.lang.Object, java.lang.Object);
-    method public static void assertNotMatchesRegex(java.lang.String, java.lang.String, java.lang.String);
-    method public static void assertNotMatchesRegex(java.lang.String, java.lang.String);
-    method public static void checkEqualsAndHashCodeMethods(java.lang.String, java.lang.Object, java.lang.Object, boolean);
-    method public static void checkEqualsAndHashCodeMethods(java.lang.Object, java.lang.Object, boolean);
-  }
-
   public abstract deprecated interface PerformanceTestCase {
     method public abstract boolean isPerformanceOnly();
     method public abstract int startPerformance(android.test.PerformanceTestCase.Intermediates);
@@ -41491,119 +41498,9 @@
     method public abstract void startTiming(boolean);
   }
 
-  public abstract deprecated class ProviderTestCase<T extends android.content.ContentProvider> extends android.test.InstrumentationTestCase {
-    ctor public ProviderTestCase(java.lang.Class<T>, java.lang.String);
-    method public android.test.mock.MockContentResolver getMockContentResolver();
-    method public android.test.IsolatedContext getMockContext();
-    method public T getProvider();
-    method public static <T extends android.content.ContentProvider> android.content.ContentResolver newResolverWithContentProviderFromSql(android.content.Context, java.lang.Class<T>, java.lang.String, java.lang.String, int, java.lang.String) throws java.lang.IllegalAccessException, java.lang.InstantiationException;
-  }
-
-  public abstract class ProviderTestCase2<T extends android.content.ContentProvider> extends android.test.AndroidTestCase {
-    ctor public ProviderTestCase2(java.lang.Class<T>, java.lang.String);
-    method public android.test.mock.MockContentResolver getMockContentResolver();
-    method public android.test.IsolatedContext getMockContext();
-    method public T getProvider();
-    method public static <T extends android.content.ContentProvider> android.content.ContentResolver newResolverWithContentProviderFromSql(android.content.Context, java.lang.String, java.lang.Class<T>, java.lang.String, java.lang.String, int, java.lang.String) throws java.lang.IllegalAccessException, java.lang.InstantiationException;
-  }
-
-  public deprecated class RenamingDelegatingContext extends android.content.ContextWrapper {
-    ctor public RenamingDelegatingContext(android.content.Context, java.lang.String);
-    ctor public RenamingDelegatingContext(android.content.Context, android.content.Context, java.lang.String);
-    method public java.lang.String getDatabasePrefix();
-    method public void makeExistingFilesAndDbsAccessible();
-    method public static <T extends android.content.ContentProvider> T providerWithRenamedContext(java.lang.Class<T>, android.content.Context, java.lang.String) throws java.lang.IllegalAccessException, java.lang.InstantiationException;
-    method public static <T extends android.content.ContentProvider> T providerWithRenamedContext(java.lang.Class<T>, android.content.Context, java.lang.String, boolean) throws java.lang.IllegalAccessException, java.lang.InstantiationException;
-  }
-
-  public abstract deprecated class ServiceTestCase<T extends android.app.Service> extends android.test.AndroidTestCase {
-    ctor public ServiceTestCase(java.lang.Class<T>);
-    method protected android.os.IBinder bindService(android.content.Intent);
-    method public android.app.Application getApplication();
-    method public T getService();
-    method public android.content.Context getSystemContext();
-    method public void setApplication(android.app.Application);
-    method protected void setupService();
-    method protected void shutdownService();
-    method protected void startService(android.content.Intent);
-    method public void testServiceTestCaseSetUpProperly() throws java.lang.Exception;
-  }
-
-  public abstract deprecated class SingleLaunchActivityTestCase<T extends android.app.Activity> extends android.test.InstrumentationTestCase {
-    ctor public SingleLaunchActivityTestCase(java.lang.String, java.lang.Class<T>);
-    method public T getActivity();
-    method public void testActivityTestCaseSetUpProperly() throws java.lang.Exception;
-  }
-
-  public deprecated class SyncBaseInstrumentation extends android.test.InstrumentationTestCase {
-    ctor public SyncBaseInstrumentation();
-    method protected void cancelSyncsandDisableAutoSync();
-    method protected void syncProvider(android.net.Uri, java.lang.String, java.lang.String) throws java.lang.Exception;
-  }
-
-  public abstract deprecated interface TestSuiteProvider {
-    method public abstract junit.framework.TestSuite getTestSuite();
-  }
-
-  public deprecated class TouchUtils {
-    ctor public TouchUtils();
-    method public static void clickView(android.test.InstrumentationTestCase, android.view.View);
-    method public static deprecated void drag(android.test.ActivityInstrumentationTestCase, float, float, float, float, int);
-    method public static void drag(android.test.InstrumentationTestCase, float, float, float, float, int);
-    method public static deprecated void dragQuarterScreenDown(android.test.ActivityInstrumentationTestCase);
-    method public static void dragQuarterScreenDown(android.test.InstrumentationTestCase, android.app.Activity);
-    method public static deprecated void dragQuarterScreenUp(android.test.ActivityInstrumentationTestCase);
-    method public static void dragQuarterScreenUp(android.test.InstrumentationTestCase, android.app.Activity);
-    method public static deprecated int dragViewBy(android.test.ActivityInstrumentationTestCase, android.view.View, int, int, int);
-    method public static deprecated int dragViewBy(android.test.InstrumentationTestCase, android.view.View, int, int, int);
-    method public static deprecated int dragViewTo(android.test.ActivityInstrumentationTestCase, android.view.View, int, int, int);
-    method public static int dragViewTo(android.test.InstrumentationTestCase, android.view.View, int, int, int);
-    method public static deprecated void dragViewToBottom(android.test.ActivityInstrumentationTestCase, android.view.View);
-    method public static void dragViewToBottom(android.test.InstrumentationTestCase, android.app.Activity, android.view.View);
-    method public static deprecated void dragViewToBottom(android.test.ActivityInstrumentationTestCase, android.view.View, int);
-    method public static void dragViewToBottom(android.test.InstrumentationTestCase, android.app.Activity, android.view.View, int);
-    method public static deprecated void dragViewToTop(android.test.ActivityInstrumentationTestCase, android.view.View);
-    method public static deprecated void dragViewToTop(android.test.ActivityInstrumentationTestCase, android.view.View, int);
-    method public static void dragViewToTop(android.test.InstrumentationTestCase, android.view.View);
-    method public static void dragViewToTop(android.test.InstrumentationTestCase, android.view.View, int);
-    method public static deprecated int dragViewToX(android.test.ActivityInstrumentationTestCase, android.view.View, int, int);
-    method public static int dragViewToX(android.test.InstrumentationTestCase, android.view.View, int, int);
-    method public static deprecated int dragViewToY(android.test.ActivityInstrumentationTestCase, android.view.View, int, int);
-    method public static int dragViewToY(android.test.InstrumentationTestCase, android.view.View, int, int);
-    method public static deprecated void longClickView(android.test.ActivityInstrumentationTestCase, android.view.View);
-    method public static void longClickView(android.test.InstrumentationTestCase, android.view.View);
-    method public static deprecated void scrollToBottom(android.test.ActivityInstrumentationTestCase, android.view.ViewGroup);
-    method public static void scrollToBottom(android.test.InstrumentationTestCase, android.app.Activity, android.view.ViewGroup);
-    method public static deprecated void scrollToTop(android.test.ActivityInstrumentationTestCase, android.view.ViewGroup);
-    method public static void scrollToTop(android.test.InstrumentationTestCase, android.app.Activity, android.view.ViewGroup);
-    method public static void tapView(android.test.InstrumentationTestCase, android.view.View);
-    method public static void touchAndCancelView(android.test.InstrumentationTestCase, android.view.View);
-  }
-
   public abstract deprecated class UiThreadTest implements java.lang.annotation.Annotation {
   }
 
-  public deprecated class ViewAsserts {
-    method public static void assertBaselineAligned(android.view.View, android.view.View);
-    method public static void assertBottomAligned(android.view.View, android.view.View);
-    method public static void assertBottomAligned(android.view.View, android.view.View, int);
-    method public static void assertGroupContains(android.view.ViewGroup, android.view.View);
-    method public static void assertGroupIntegrity(android.view.ViewGroup);
-    method public static void assertGroupNotContains(android.view.ViewGroup, android.view.View);
-    method public static void assertHasScreenCoordinates(android.view.View, android.view.View, int, int);
-    method public static void assertHorizontalCenterAligned(android.view.View, android.view.View);
-    method public static void assertLeftAligned(android.view.View, android.view.View);
-    method public static void assertLeftAligned(android.view.View, android.view.View, int);
-    method public static void assertOffScreenAbove(android.view.View, android.view.View);
-    method public static void assertOffScreenBelow(android.view.View, android.view.View);
-    method public static void assertOnScreen(android.view.View, android.view.View);
-    method public static void assertRightAligned(android.view.View, android.view.View);
-    method public static void assertRightAligned(android.view.View, android.view.View, int);
-    method public static void assertTopAligned(android.view.View, android.view.View);
-    method public static void assertTopAligned(android.view.View, android.view.View, int);
-    method public static void assertVerticalCenterAligned(android.view.View, android.view.View);
-  }
-
 }
 
 package android.test.mock {
@@ -41899,37 +41796,6 @@
 
 }
 
-package android.test.suitebuilder {
-
-  public deprecated class TestMethod {
-    ctor public TestMethod(java.lang.reflect.Method, java.lang.Class<? extends junit.framework.TestCase>);
-    ctor public TestMethod(java.lang.String, java.lang.Class<? extends junit.framework.TestCase>);
-    ctor public TestMethod(junit.framework.TestCase);
-    method public junit.framework.TestCase createTest() throws java.lang.IllegalAccessException, java.lang.InstantiationException, java.lang.reflect.InvocationTargetException;
-    method public <T extends java.lang.annotation.Annotation> T getAnnotation(java.lang.Class<T>);
-    method public java.lang.Class<? extends junit.framework.TestCase> getEnclosingClass();
-    method public java.lang.String getEnclosingClassname();
-    method public java.lang.String getName();
-  }
-
-  public deprecated class TestSuiteBuilder {
-    ctor public TestSuiteBuilder(java.lang.Class);
-    ctor public TestSuiteBuilder(java.lang.String, java.lang.ClassLoader);
-    method public final junit.framework.TestSuite build();
-    method public android.test.suitebuilder.TestSuiteBuilder excludePackages(java.lang.String...);
-    method protected java.lang.String getSuiteName();
-    method public final android.test.suitebuilder.TestSuiteBuilder includeAllPackagesUnderHere();
-    method public android.test.suitebuilder.TestSuiteBuilder includePackages(java.lang.String...);
-    method public android.test.suitebuilder.TestSuiteBuilder named(java.lang.String);
-  }
-
-  public static deprecated class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase {
-    ctor public TestSuiteBuilder.FailedToCreateTests(java.lang.Exception);
-    method public void testSuiteConstructionFailed();
-  }
-
-}
-
 package android.test.suitebuilder.annotation {
 
   public abstract deprecated class LargeTest implements java.lang.annotation.Annotation {
@@ -44574,6 +44440,12 @@
     field public static final int[] WILD_CARD;
   }
 
+  public final class StatsLog {
+    method public static boolean logEvent(int);
+    method public static boolean logStart(int);
+    method public static boolean logStop(int);
+  }
+
   public class StringBuilderPrinter implements android.util.Printer {
     ctor public StringBuilderPrinter(java.lang.StringBuilder);
     method public void println(java.lang.String);
@@ -47067,6 +46939,7 @@
     method public int getScaledEdgeSlop();
     method public int getScaledFadingEdgeLength();
     method public float getScaledHorizontalScrollFactor();
+    method public int getScaledHoverSlop();
     method public int getScaledMaximumDrawingCacheSize();
     method public int getScaledMaximumFlingVelocity();
     method public int getScaledMinimumFlingVelocity();
@@ -48753,9 +48626,11 @@
     method public void cancel();
     method public void commit();
     method public void disableAutofillServices();
+    method public android.service.autofill.UserData getUserData();
     method public boolean hasEnabledAutofillServices();
     method public boolean isAutofillSupported();
     method public boolean isEnabled();
+    method public boolean isFieldClassificationEnabled();
     method public void notifyValueChanged(android.view.View);
     method public void notifyValueChanged(android.view.View, int, android.view.autofill.AutofillValue);
     method public void notifyViewEntered(android.view.View);
@@ -48767,6 +48642,7 @@
     method public void registerCallback(android.view.autofill.AutofillManager.AutofillCallback);
     method public void requestAutofill(android.view.View);
     method public void requestAutofill(android.view.View, int, android.graphics.Rect);
+    method public void setUserData(android.service.autofill.UserData);
     method public void unregisterCallback(android.view.autofill.AutofillManager.AutofillCallback);
     field public static final java.lang.String EXTRA_ASSIST_STRUCTURE = "android.view.autofill.extra.ASSIST_STRUCTURE";
     field public static final java.lang.String EXTRA_AUTHENTICATION_RESULT = "android.view.autofill.extra.AUTHENTICATION_RESULT";
diff --git a/api/system-current.txt b/api/system-current.txt
index 30e4cbc..227b483 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3531,6 +3531,11 @@
   public static final class Settings.Secure extends android.provider.Settings.NameValueTable {
     method public static boolean putString(android.content.ContentResolver, java.lang.String, java.lang.String, java.lang.String, boolean);
     method public static void resetToDefaults(android.content.ContentResolver, java.lang.String);
+    field public static final java.lang.String AUTOFILL_FEATURE_FIELD_CLASSIFICATION = "autofill_field_classification";
+    field public static final java.lang.String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE = "autofill_user_data_max_field_classification_size";
+    field public static final java.lang.String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE = "autofill_user_data_max_user_data_size";
+    field public static final java.lang.String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH = "autofill_user_data_max_value_length";
+    field public static final java.lang.String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH = "autofill_user_data_min_value_length";
     field public static final java.lang.String INSTANT_APPS_ENABLED = "instant_apps_enabled";
   }
 
diff --git a/api/test-current.txt b/api/test-current.txt
index 3fc5cd6..5635b56 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -463,34 +463,11 @@
   }
 
   public final class EditDistanceScorer extends android.service.autofill.InternalScorer implements android.os.Parcelable android.service.autofill.Scorer {
-    method public int describeContents();
-    method public static android.service.autofill.EditDistanceScorer getInstance();
     method public float getScore(android.view.autofill.AutofillValue, java.lang.String);
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.EditDistanceScorer> CREATOR;
   }
 
-  public final class FieldClassification implements android.os.Parcelable {
-    method public int describeContents();
-    method public java.util.List<android.service.autofill.FieldClassification.Match> getMatches();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.FieldClassification> CREATOR;
-  }
-
-  public static final class FieldClassification.Match implements android.os.Parcelable {
-    method public int describeContents();
-    method public java.lang.String getRemoteId();
-    method public float getScore();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.FieldClassification.Match> CREATOR;
-  }
-
-  public static final class FillEventHistory.Event {
-    method public java.util.Map<android.view.autofill.AutofillId, android.service.autofill.FieldClassification> getFieldsClassification();
-  }
-
-  public static final class FillResponse.Builder {
-    method public android.service.autofill.FillResponse.Builder setFieldClassificationIds(android.view.autofill.AutofillId...);
+  public final class FillResponse implements android.os.Parcelable {
+    method public int getFlags();
   }
 
   public final class ImageTransformation extends android.service.autofill.InternalTransformation implements android.os.Parcelable android.service.autofill.Transformation {
@@ -523,29 +500,10 @@
     method public boolean isValid(android.service.autofill.ValueFinder);
   }
 
-  public abstract interface Scorer {
-  }
-
   public final class TextValueSanitizer extends android.service.autofill.InternalSanitizer implements android.os.Parcelable android.service.autofill.Sanitizer {
     method public android.view.autofill.AutofillValue sanitize(android.view.autofill.AutofillValue);
   }
 
-  public final class UserData implements android.os.Parcelable {
-    method public int describeContents();
-    method public static int getMaxFieldClassificationIdsSize();
-    method public static int getMaxUserDataSize();
-    method public static int getMaxValueLength();
-    method public static int getMinValueLength();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator<android.service.autofill.UserData> CREATOR;
-  }
-
-  public static final class UserData.Builder {
-    ctor public UserData.Builder(android.service.autofill.Scorer, java.lang.String, java.lang.String);
-    method public android.service.autofill.UserData.Builder add(java.lang.String, java.lang.String);
-    method public android.service.autofill.UserData build();
-  }
-
   public abstract interface ValueFinder {
     method public abstract java.lang.String findByAutofillId(android.view.autofill.AutofillId);
   }
@@ -993,12 +951,6 @@
     ctor public AutofillId(int);
   }
 
-  public final class AutofillManager {
-    method public android.service.autofill.UserData getUserData();
-    method public boolean isFieldClassificationEnabled();
-    method public void setUserData(android.service.autofill.UserData);
-  }
-
 }
 
 package android.widget {
diff --git a/cmds/ime/ime b/cmds/ime/ime
index 180dc76..7d2f72f 100755
--- a/cmds/ime/ime
+++ b/cmds/ime/ime
@@ -1,2 +1,2 @@
 #!/system/bin/sh
-exec cmd input_method "$@"
+exec cmd input_method ime "$@"
diff --git a/cmds/statsd/src/anomaly/AnomalyTracker.cpp b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
index e8b4083..4777dcd 100644
--- a/cmds/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/cmds/statsd/src/anomaly/AnomalyTracker.cpp
@@ -317,7 +317,7 @@
     for (const auto& kv : matchedAlarms) {
         declareAnomaly(timestampNs /* TODO: , kv.first */);
         mAlarms.erase(kv.first);
-        firedAlarms.erase(kv.second); // No one else can also own it, so we're done with it.
+        firedAlarms.erase(kv.second);  // No one else can also own it, so we're done with it.
     }
 }
 
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 716fee6..a39acb2 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -79,6 +79,7 @@
         IsolatedUidChanged isolated_uid_changed = 43;
         PacketWakeupOccurred packet_wakeup_occurred = 44;
         DropboxErrorChanged dropbox_error_changed = 45;
+        AppHook app_hook = 46;
         // TODO: Reorder the numbering so that the most frequent occur events occur in the first 15.
     }
 
@@ -97,19 +98,29 @@
         CpuTimePerUidFreqPulled cpu_time_per_uid_freq_pulled = 1010;
         WifiActivityEnergyInfoPulled wifi_activity_energy_info_pulled = 1011;
         ModemActivityInfoPulled modem_activity_info_pulled = 1012;
+        AttributionChainDummyAtom attribution_chain_dummy_atom = 10000;
     }
 }
 
 /**
- * A WorkSource represents the chained attribution of applications that
- * resulted in a particular bit of work being done.
+ * An attribution represents an application or module that is part of process where a particular bit
+ * of work is done.
  */
-message WorkSource {
-    // The uid for a given element in the attribution chain.
-    repeated int32 uid = 1;
-    // The (optional) string tag for an element in the attribution chain. If the
-    // element has no tag, it is encoded as an empty string.
-    repeated string tag = 2;
+message Attribution {
+    // The uid for an application or module.
+    optional int32 uid = 1;
+    // The string tag for the attribution node.
+    optional string tag = 2;
+}
+
+/**
+ * An attribution chain represents the chained attributions of applications or modules that
+ * resulted in a particular bit of work being done.
+ * The ordering of the attributions is that of calls, that is uid = [A, B, C] if A calls B that
+ * calls C.
+ */
+message AttributionChain {
+    repeated Attribution attribution = 1;
 }
 
 /*
@@ -127,7 +138,7 @@
  *   - The CamelCase name of the message type should match the
  *     underscore_separated name as defined in Atom.
  *   - If an atom represents work that can be attributed to an app, there can
- *     be exactly one WorkSource field. It must be field number 1.
+ *     be exactly one AttributionChain field. It must be field number 1.
  *   - A field that is a uid should be a string field, tagged with the [xxx]
  *     annotation. The generated code on android will be represented by UIDs,
  *     and those UIDs will be translated in xxx to those strings.
@@ -138,6 +149,11 @@
  * *****************************************************************************
  */
 
+message AttributionChainDummyAtom {
+    optional AttributionChain attribution_chain = 1;
+    optional int32 value = 2;
+}
+
 /**
  * Logs when the screen state changes.
  *
@@ -801,6 +817,29 @@
     optional int32 is_foreground = 7;
 }
 
+/*
+ * Allows other apps to push events into statsd.
+ * Logged from:
+ *      frameworks/base/core/java/android/util/StatsLog.java
+ */
+message AppHook {
+    // The uid of the application that sent this custom atom.
+    optional int32 uid = 1;
+
+    // An arbitrary label chosen by the developer. For Android P, the label should be in [0, 16).
+    optional int32 label = 2;
+
+    // Allows applications to easily use a custom event as start/stop boundaries (ie, define custom
+    // predicates for the metrics).
+    enum State {
+        UNKNOWN = 0;
+        UNSPECIFIED = 1;  // For events that are known to not represent START/STOP.
+        STOP = 2;
+        START = 3;
+    }
+    optional State state = 3;
+}
+
 /**
  * Pulls bytes transferred via wifi (Sum of foreground and background usage).
  *
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index e52b273..164f88f 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -29,6 +29,12 @@
 namespace os {
 namespace statsd {
 
+using std::map;
+using std::pair;
+using std::set;
+using std::string;
+using std::vector;
+
 #define STATS_SERVICE_DIR "/data/misc/stats-service"
 
 using android::base::StringPrintf;
@@ -41,8 +47,14 @@
 }
 
 void ConfigManager::Startup() {
-    StorageManager::readConfigFromDisk(mConfigs);
-
+    map<ConfigKey, StatsdConfig> configsFromDisk;
+    StorageManager::readConfigFromDisk(configsFromDisk);
+    // TODO(b/70667694): Make the configs from disk be used. And remove the fake config,
+    // and tests shouldn't call this Startup(), maybe call StartupForTest() so we don't read
+    // configs from disk for tests.
+    // for (const auto& pair : configsFromDisk) {
+    //    UpdateConfig(pair.first, pair.second);
+    //}
     // this should be called from StatsService when it receives a statsd_config
     UpdateConfig(ConfigKey(1000, "fake"), build_fake_config());
 }
@@ -52,9 +64,8 @@
 }
 
 void ConfigManager::UpdateConfig(const ConfigKey& key, const StatsdConfig& config) {
-    // Add to map
-    mConfigs[key] = config;
-    // Why doesn't this work? mConfigs.insert({key, config});
+    // Add to set
+    mConfigs.insert(key);
 
     // Save to disk
     update_saved_configs(key, config);
@@ -74,7 +85,7 @@
 }
 
 void ConfigManager::RemoveConfig(const ConfigKey& key) {
-    unordered_map<ConfigKey, StatsdConfig>::iterator it = mConfigs.find(key);
+    auto it = mConfigs.find(key);
     if (it != mConfigs.end()) {
         // Remove from map
         mConfigs.erase(it);
@@ -100,9 +111,9 @@
 
     for (auto it = mConfigs.begin(); it != mConfigs.end();) {
         // Remove from map
-        if (it->first.GetUid() == uid) {
-            removed.push_back(it->first);
-            mConfigReceivers.erase(it->first);
+        if (it->GetUid() == uid) {
+            removed.push_back(*it);
+            mConfigReceivers.erase(*it);
             it = mConfigs.erase(it);
         } else {
             it++;
@@ -123,10 +134,10 @@
 
     for (auto it = mConfigs.begin(); it != mConfigs.end();) {
         // Remove from map
-        removed.push_back(it->first);
-        auto receiverIt = mConfigReceivers.find(it->first);
+        removed.push_back(*it);
+        auto receiverIt = mConfigReceivers.find(*it);
         if (receiverIt != mConfigReceivers.end()) {
-            mConfigReceivers.erase(it->first);
+            mConfigReceivers.erase(*it);
         }
         it = mConfigs.erase(it);
     }
@@ -143,7 +154,7 @@
 vector<ConfigKey> ConfigManager::GetAllConfigKeys() const {
     vector<ConfigKey> ret;
     for (auto it = mConfigs.cbegin(); it != mConfigs.cend(); ++it) {
-        ret.push_back(it->first);
+        ret.push_back(*it);
     }
     return ret;
 }
@@ -160,15 +171,13 @@
 void ConfigManager::Dump(FILE* out) {
     fprintf(out, "CONFIGURATIONS (%d)\n", (int)mConfigs.size());
     fprintf(out, "     uid name\n");
-    for (unordered_map<ConfigKey, StatsdConfig>::const_iterator it = mConfigs.begin();
-         it != mConfigs.end(); it++) {
-        fprintf(out, "  %6d %s\n", it->first.GetUid(), it->first.GetName().c_str());
-        auto receiverIt = mConfigReceivers.find(it->first);
+    for (const auto& key : mConfigs) {
+        fprintf(out, "  %6d %s\n", key.GetUid(), key.GetName().c_str());
+        auto receiverIt = mConfigReceivers.find(key);
         if (receiverIt != mConfigReceivers.end()) {
             fprintf(out, "    -> received by %s, %s\n", receiverIt->second.first.c_str(),
                     receiverIt->second.second.c_str());
         }
-        // TODO: Print the contents of the config too.
     }
 }
 
@@ -227,7 +236,8 @@
     metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
 
     // Anomaly threshold for screen-on count.
-    Alert* alert = config.add_alert();
+    // TODO(b/70627390): Uncomment once the bug is fixed.
+    /*Alert* alert = config.add_alert();
     alert->set_name("ALERT_1");
     alert->set_metric_name("METRIC_1");
     alert->set_number_of_buckets(6);
@@ -235,7 +245,7 @@
     alert->set_refractory_period_secs(30);
     Alert::IncidentdDetails* details = alert->mutable_incidentd_details();
     details->add_section(12);
-    details->add_section(13);
+    details->add_section(13);*/
 
     // Count process state changes, slice by uid.
     metric = config.add_count_metric();
@@ -246,6 +256,8 @@
     keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY);
 
     // Anomaly threshold for background count.
+    // TODO(b/70627390): Uncomment once the bug is fixed.
+    /*
     alert = config.add_alert();
     alert->set_name("ALERT_2");
     alert->set_metric_name("METRIC_2");
@@ -254,7 +266,7 @@
     alert->set_refractory_period_secs(20);
     details = alert->mutable_incidentd_details();
     details->add_section(14);
-    details->add_section(15);
+    details->add_section(15);*/
 
     // Count process state changes, slice by uid, while SCREEN_IS_OFF
     metric = config.add_count_metric();
@@ -326,6 +338,8 @@
     durationMetric->set_what("SCREEN_IS_ON");
 
     // Anomaly threshold for background count.
+    // TODO(b/70627390): Uncomment once the bug is fixed.
+    /*
     alert = config.add_alert();
     alert->set_name("ALERT_8");
     alert->set_metric_name("METRIC_8");
@@ -333,7 +347,7 @@
     alert->set_trigger_if_sum_gt(2000000000); // 2 seconds
     alert->set_refractory_period_secs(120);
     details = alert->mutable_incidentd_details();
-    details->add_section(-1);
+    details->add_section(-1);*/
 
     // Value metric to count KERNEL_WAKELOCK when screen turned on
     ValueMetric* valueMetric = config.add_value_metric();
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index 90ea6c0..ea42a35 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -19,8 +19,9 @@
 #include "config/ConfigKey.h"
 #include "config/ConfigListener.h"
 
+#include <map>
+#include <set>
 #include <string>
-#include <unordered_map>
 
 #include <stdio.h>
 
@@ -28,13 +29,7 @@
 namespace os {
 namespace statsd {
 
-using android::RefBase;
-using std::string;
-using std::unordered_map;
-using std::vector;
-using std::pair;
-
-// Util function to Hard code a test metric for counting screen on events.
+// Util function to build a hard coded config with test metrics.
 StatsdConfig build_fake_config();
 
 /**
@@ -43,7 +38,7 @@
  * TODO: Store the configs persistently too.
  * TODO: Dump method for debugging.
  */
-class ConfigManager : public virtual RefBase {
+class ConfigManager : public virtual android::RefBase {
 public:
     ConfigManager();
     virtual ~ConfigManager();
@@ -68,17 +63,17 @@
     /**
      * Sets the broadcast receiver for a configuration key.
      */
-    void SetConfigReceiver(const ConfigKey& key, const string& pkg, const string& cls);
+    void SetConfigReceiver(const ConfigKey& key, const std::string& pkg, const std::string& cls);
 
     /**
      * Returns the package name and class name representing the broadcast receiver for this config.
      */
-    const pair<string, string> GetConfigReceiver(const ConfigKey& key) const;
+    const std::pair<std::string, std::string> GetConfigReceiver(const ConfigKey& key) const;
 
     /**
      * Returns all config keys registered.
      */
-    vector<ConfigKey> GetAllConfigKeys() const;
+    std::vector<ConfigKey> GetAllConfigKeys() const;
 
     /**
      * Erase any broadcast receiver associated with this config key.
@@ -121,18 +116,18 @@
     /**
      * The Configs that have been set. Each config should
      */
-    unordered_map<ConfigKey, StatsdConfig> mConfigs;
+    std::set<ConfigKey> mConfigs;
 
     /**
      * Each config key can be subscribed by up to one receiver, specified as the package name and
      * class name.
      */
-    unordered_map<ConfigKey, pair<string, string>> mConfigReceivers;
+    std::map<ConfigKey, std::pair<std::string, std::string>> mConfigReceivers;
 
     /**
      * The ConfigListeners that will be told about changes.
      */
-    vector<sp<ConfigListener>> mListeners;
+    std::vector<sp<ConfigListener>> mListeners;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index ae297d9..7b865c2 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -66,7 +66,7 @@
                                          const int conditionIndex,
                                          const sp<ConditionWizard>& wizard,
                                          const uint64_t startTimeNs)
-    : MetricProducer(key, startTimeNs, conditionIndex, wizard), mMetric(metric) {
+    : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard) {
     // TODO: evaluate initial conditions. and set mConditionMet.
     if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
         mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
@@ -92,18 +92,18 @@
 }
 
 void CountMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
-    VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
+    VLOG("Metric %s onSlicedConditionMayChange", mName.c_str());
 }
 
 void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
                                              ProtoOutputStream* protoOutput) {
     flushIfNeededLocked(dumpTimeNs);
 
-    protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
+    protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
     long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_COUNT_METRICS);
 
-    VLOG("metric %s dump report now...", mMetric.name().c_str());
+    VLOG("metric %s dump report now...", mName.c_str());
 
     for (const auto& counter : mPastBuckets) {
         const HashableDimensionKey& hashableKey = counter.first;
@@ -160,7 +160,7 @@
 
 void CountMetricProducer::onConditionChangedLocked(const bool conditionMet,
                                                    const uint64_t eventTime) {
-    VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
+    VLOG("Metric %s onConditionChanged", mName.c_str());
     mCondition = conditionMet;
 }
 
@@ -172,11 +172,10 @@
     // 1. Report the tuple count if the tuple count > soft limit
     if (mCurrentSlicedCounter->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mCurrentSlicedCounter->size() + 1;
-        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
-                                                           newTupleCount);
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
-            ALOGE("CountMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
+            ALOGE("CountMetric %s dropping data for dimension key %s", mName.c_str(),
                   newKey.c_str());
             return true;
         }
@@ -218,7 +217,7 @@
                                          mCurrentSlicedCounter->find(eventKey)->second);
     }
 
-    VLOG("metric %s %s->%lld", mMetric.name().c_str(), eventKey.c_str(),
+    VLOG("metric %s %s->%lld", mName.c_str(), eventKey.c_str(),
          (long long)(*mCurrentSlicedCounter)[eventKey]);
 }
 
@@ -237,7 +236,7 @@
         info.mCount = counter.second;
         auto& bucketList = mPastBuckets[counter.first];
         bucketList.push_back(info);
-        VLOG("metric %s, dump key value: %s -> %lld", mMetric.name().c_str(), counter.first.c_str(),
+        VLOG("metric %s, dump key value: %s -> %lld", mName.c_str(), counter.first.c_str(),
              (long long)counter.second);
     }
 
@@ -250,7 +249,7 @@
     uint64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
     mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
     mCurrentBucketNum += numBucketsForward;
-    VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
+    VLOG("metric %s: new bucket start time: %lld", mName.c_str(),
          (long long)mCurrentBucketStartTimeNs);
 }
 
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 8a17169..59995d2 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -76,8 +76,6 @@
     // Util function to flush the old packet.
     void flushIfNeededLocked(const uint64_t& newEventTime);
 
-    const CountMetric mMetric;
-
     // TODO: Add a lock to mPastBuckets.
     std::unordered_map<HashableDimensionKey, std::vector<CountBucket>> mPastBuckets;
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index c268798..6afbe45 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -68,8 +68,8 @@
                                                const sp<ConditionWizard>& wizard,
                                                const vector<KeyMatcher>& internalDimension,
                                                const uint64_t startTimeNs)
-    : MetricProducer(key, startTimeNs, conditionIndex, wizard),
-      mMetric(metric),
+    : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard),
+      mAggregationType(metric.aggregation_type()),
       mStartIndex(startIndex),
       mStopIndex(stopIndex),
       mStopAllIndex(stopAllIndex),
@@ -114,20 +114,20 @@
 
 unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
         const HashableDimensionKey& eventKey) const {
-    switch (mMetric.aggregation_type()) {
+    switch (mAggregationType) {
         case DurationMetric_AggregationType_SUM:
             return make_unique<OringDurationTracker>(
-                    mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested,
+                    mConfigKey, mName, eventKey, mWizard, mConditionTrackerIndex, mNested,
                     mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers);
         case DurationMetric_AggregationType_MAX_SPARSE:
             return make_unique<MaxDurationTracker>(
-                    mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested,
+                    mConfigKey, mName, eventKey, mWizard, mConditionTrackerIndex, mNested,
                     mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers);
     }
 }
 
 void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
-    VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
+    VLOG("Metric %s onSlicedConditionMayChange", mName.c_str());
     flushIfNeededLocked(eventTime);
     // Now for each of the on-going event, check if the condition has changed for them.
     for (auto& pair : mCurrentSlicedDuration) {
@@ -137,7 +137,7 @@
 
 void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
                                                       const uint64_t eventTime) {
-    VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
+    VLOG("Metric %s onConditionChanged", mName.c_str());
     mCondition = conditionMet;
     flushIfNeededLocked(eventTime);
     // TODO: need to populate the condition change time from the event which triggers the condition
@@ -151,11 +151,11 @@
                                                 ProtoOutputStream* protoOutput) {
     flushIfNeededLocked(dumpTimeNs);
 
-    protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
+    protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
     long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_DURATION_METRICS);
 
-    VLOG("metric %s dump report now...", mMetric.name().c_str());
+    VLOG("metric %s dump report now...", mName.c_str());
 
     for (const auto& pair : mPastBuckets) {
         const HashableDimensionKey& hashableKey = pair.first;
@@ -236,11 +236,10 @@
     // 1. Report the tuple count if the tuple count > soft limit
     if (mCurrentSlicedDuration.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mCurrentSlicedDuration.size() + 1;
-        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
-                                                           newTupleCount);
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
-            ALOGE("DurationMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
+            ALOGE("DurationMetric %s dropping data for dimension key %s", mName.c_str(),
                   newKey.c_str());
             return true;
         }
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 14504c1..e7aca7f 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -75,7 +75,7 @@
     // Util function to flush the old packet.
     void flushIfNeededLocked(const uint64_t& eventTime);
 
-    const DurationMetric mMetric;
+    const DurationMetric_AggregationType mAggregationType;
 
     // Index of the SimpleAtomMatcher which defines the start.
     const size_t mStartIndex;
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index bcecf16..4752997 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -55,7 +55,7 @@
                                          const int conditionIndex,
                                          const sp<ConditionWizard>& wizard,
                                          const uint64_t startTimeNs)
-    : MetricProducer(key, startTimeNs, conditionIndex, wizard), mMetric(metric) {
+    : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard) {
     if (metric.links().size() > 0) {
         mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
                                metric.links().end());
@@ -98,12 +98,12 @@
 
 void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
                                              ProtoOutputStream* protoOutput) {
-    protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
+    protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
 
     size_t bufferSize = mProto->size();
-    VLOG("metric %s dump report now... proto size: %zu ", mMetric.name().c_str(), bufferSize);
+    VLOG("metric %s dump report now... proto size: %zu ", mName.c_str(), bufferSize);
     std::unique_ptr<std::vector<uint8_t>> buffer = serializeProtoLocked(*mProto);
 
     protoOutput->write(FIELD_TYPE_MESSAGE | FIELD_ID_EVENT_METRICS,
@@ -115,7 +115,7 @@
 
 void EventMetricProducer::onConditionChangedLocked(const bool conditionMet,
                                                    const uint64_t eventTime) {
-    VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
+    VLOG("Metric %s onConditionChanged", mName.c_str());
     mCondition = conditionMet;
 }
 
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 49ba9d8..d720ead 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -67,8 +67,6 @@
     // Internal function to calculate the current used bytes.
     size_t byteSizeLocked() const override;
 
-    const EventMetric mMetric;
-
     // Maps to a EventMetricDataWrapper. Storing atom events in ProtoOutputStream
     // is more space efficient than storing LogEvent.
     std::unique_ptr<android::util::ProtoOutputStream> mProto;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index fffb2bf..ae9b86f 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -67,8 +67,8 @@
                                          const int conditionIndex,
                                          const sp<ConditionWizard>& wizard, const int pullTagId,
                                          const int64_t startTimeNs)
-    : MetricProducer(key, startTimeNs, conditionIndex, wizard),
-      mMetric(metric),
+    : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard),
+      mGaugeField(metric.gauge_field()),
       mPullTagId(pullTagId) {
     if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
         mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
@@ -104,11 +104,11 @@
 
 void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
                                              ProtoOutputStream* protoOutput) {
-    VLOG("gauge metric %s dump report now...", mMetric.name().c_str());
+    VLOG("gauge metric %s dump report now...", mName.c_str());
 
     flushIfNeededLocked(dumpTimeNs);
 
-    protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
+    protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
     long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
 
@@ -166,7 +166,7 @@
 
 void GaugeMetricProducer::onConditionChangedLocked(const bool conditionMet,
                                                    const uint64_t eventTime) {
-    VLOG("Metric %s onConditionChanged", mMetric.name().c_str());
+    VLOG("Metric %s onConditionChanged", mName.c_str());
     flushIfNeededLocked(eventTime);
     mCondition = conditionMet;
 
@@ -193,12 +193,12 @@
 }
 
 void GaugeMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
-    VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
+    VLOG("Metric %s onSlicedConditionMayChange", mName.c_str());
 }
 
 int64_t GaugeMetricProducer::getGauge(const LogEvent& event) {
     status_t err = NO_ERROR;
-    int64_t val = event.GetLong(mMetric.gauge_field(), &err);
+    int64_t val = event.GetLong(mGaugeField, &err);
     if (err == NO_ERROR) {
         return val;
     } else {
@@ -222,11 +222,10 @@
     // 1. Report the tuple count if the tuple count > soft limit
     if (mCurrentSlicedBucket->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mCurrentSlicedBucket->size() + 1;
-        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
-                                                           newTupleCount);
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
-            ALOGE("GaugeMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
+            ALOGE("GaugeMetric %s dropping data for dimension key %s", mName.c_str(),
                   newKey.c_str());
             return true;
         }
@@ -289,8 +288,8 @@
         info.mGauge = slice.second;
         auto& bucketList = mPastBuckets[slice.first];
         bucketList.push_back(info);
-        VLOG("gauge metric %s, dump key value: %s -> %lld", mMetric.name().c_str(),
-             slice.first.c_str(), (long long)slice.second);
+        VLOG("gauge metric %s, dump key value: %s -> %lld", mName.c_str(), slice.first.c_str(),
+             (long long)slice.second);
     }
 
     // Reset counters
@@ -304,7 +303,7 @@
     int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
     mCurrentBucketStartTimeNs = mCurrentBucketStartTimeNs + numBucketsForward * mBucketSizeNs;
     mCurrentBucketNum += numBucketsForward;
-    VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
+    VLOG("metric %s: new bucket start time: %lld", mName.c_str(),
          (long long)mCurrentBucketStartTimeNs);
 }
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index ee4f40c..6e6f2bb 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -87,7 +87,7 @@
     // The default bucket size for gauge metric is 1 second.
     static const uint64_t kDefaultGaugemBucketSizeNs = 1000 * 1000 * 1000;
 
-    const GaugeMetric mMetric;
+    const int32_t mGaugeField;
 
     StatsPullerManager mStatsPullerManager;
     // tagId for pulled data. -1 if this is not pulled
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 269bd43..d4a2195 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -38,9 +38,10 @@
 // be a no-op.
 class MetricProducer : public virtual PackageInfoListener {
 public:
-    MetricProducer(const ConfigKey& key, const int64_t startTimeNs, const int conditionIndex,
-                   const sp<ConditionWizard>& wizard)
-        : mConfigKey(key),
+    MetricProducer(const std::string& name, const ConfigKey& key, const int64_t startTimeNs,
+                   const int conditionIndex, const sp<ConditionWizard>& wizard)
+        : mName(name),
+          mConfigKey(key),
           mStartTimeNs(startTimeNs),
           mCurrentBucketStartTimeNs(startTimeNs),
           mCurrentBucketNum(0),
@@ -108,6 +109,8 @@
                                     android::util::ProtoOutputStream* protoOutput) = 0;
     virtual size_t byteSizeLocked() const = 0;
 
+    const std::string mName;
+
     const ConfigKey mConfigKey;
 
     // The start time for the current in memory metrics data.
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index b0f0135..3d0e20c 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -50,11 +50,6 @@
                              mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
                              mTrackerToMetricMap, mTrackerToConditionMap);
 
-    // TODO: add alert size.
-    // no matter whether this config is valid, log it in the stats.
-    StatsdStats::getInstance().noteConfigReceived(key, mAllMetricProducers.size(),
-                                                  mAllConditionTrackers.size(),
-                                                  mAllAtomMatchers.size(), 0, mConfigValid);
     // Guardrail. Reject the config if it's too big.
     if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig ||
         mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig ||
@@ -62,6 +57,12 @@
         ALOGE("This config is too big! Reject!");
         mConfigValid = false;
     }
+
+    // TODO: add alert size.
+    // no matter whether this config is valid, log it in the stats.
+    StatsdStats::getInstance().noteConfigReceived(key, mAllMetricProducers.size(),
+                                                  mAllConditionTrackers.size(),
+                                                  mAllAtomMatchers.size(), 0, mConfigValid);
 }
 
 MetricsManager::~MetricsManager() {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index aabe5af..1eabf11 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -73,13 +73,13 @@
                                          const sp<ConditionWizard>& wizard, const int pullTagId,
                                          const uint64_t startTimeNs,
                                          shared_ptr<StatsPullerManager> statsPullerManager)
-    : MetricProducer(key, startTimeNs, conditionIndex, wizard),
-      mMetric(metric),
+    : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard),
+      mValueField(metric.value_field()),
       mStatsPullerManager(statsPullerManager),
       mPullTagId(pullTagId) {
     // TODO: valuemetric for pushed events may need unlimited bucket length
     if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
-        mBucketSizeNs = mMetric.bucket().bucket_size_millis() * 1000 * 1000;
+        mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
     } else {
         mBucketSizeNs = kDefaultBucketSizeMillis * 1000 * 1000;
     }
@@ -118,14 +118,14 @@
 }
 
 void ValueMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
-    VLOG("Metric %s onSlicedConditionMayChange", mMetric.name().c_str());
+    VLOG("Metric %s onSlicedConditionMayChange", mName.c_str());
 }
 
 void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
                                              ProtoOutputStream* protoOutput) {
-    VLOG("metric %s dump report now...", mMetric.name().c_str());
+    VLOG("metric %s dump report now...", mName.c_str());
     flushIfNeededLocked(dumpTimeNs);
-    protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mMetric.name());
+    protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_START_REPORT_NANOS, (long long)mStartTimeNs);
     long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
 
@@ -175,7 +175,7 @@
     protoOutput->end(protoToken);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_REPORT_NANOS, (long long)dumpTimeNs);
 
-    VLOG("metric %s dump report now...", mMetric.name().c_str());
+    VLOG("metric %s dump report now...", mName.c_str());
     mPastBuckets.clear();
     mStartTimeNs = mCurrentBucketStartTimeNs;
     // TODO: Clear mDimensionKeyMap once the report is dumped.
@@ -194,8 +194,7 @@
 
     if (mPullTagId != -1) {
         if (mCondition == true) {
-            mStatsPullerManager->RegisterReceiver(mPullTagId, this,
-                                                  mMetric.bucket().bucket_size_millis());
+            mStatsPullerManager->RegisterReceiver(mPullTagId, this, mBucketSizeNs / 1000 / 1000);
         } else if (mCondition == false) {
             mStatsPullerManager->UnRegisterReceiver(mPullTagId, this);
         }
@@ -216,7 +215,7 @@
 void ValueMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
     std::lock_guard<std::mutex> lock(mMutex);
 
-    if (mCondition == true || !mMetric.has_condition()) {
+    if (mCondition == true || mConditionTrackerIndex < 0) {
         if (allData.size() == 0) {
             return;
         }
@@ -247,11 +246,10 @@
     }
     if (mCurrentSlicedBucket.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mCurrentSlicedBucket.size() + 1;
-        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
-                                                           newTupleCount);
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName, newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
-            ALOGE("ValueMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
+            ALOGE("ValueMetric %s dropping data for dimension key %s", mName.c_str(),
                   newKey.c_str());
             return true;
         }
@@ -296,11 +294,15 @@
     } else {    // for pushed events
         interval.sum += value;
     }
+
+    for (auto& tracker : mAnomalyTrackers) {
+        tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, interval.sum);
+    }
 }
 
 long ValueMetricProducer::get_value(const LogEvent& event) {
     status_t err = NO_ERROR;
-    long val = event.GetLong(mMetric.value_field(), &err);
+    long val = event.GetLong(mValueField, &err);
     if (err == NO_ERROR) {
         return val;
     } else {
@@ -329,6 +331,12 @@
         // it will auto create new vector of ValuebucketInfo if the key is not found.
         auto& bucketList = mPastBuckets[slice.first];
         bucketList.push_back(info);
+
+        for (auto& tracker : mAnomalyTrackers) {
+            if (tracker != nullptr) {
+                tracker->addPastBucket(slice.first, info.mValue, info.mBucketNum);
+            }
+        }
     }
     VLOG("%d tainted pairs in the bucket", tainted);
 
@@ -342,7 +350,7 @@
     if (numBucketsForward > 1) {
         VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
     }
-    VLOG("metric %s: new bucket start time: %lld", mMetric.name().c_str(),
+    VLOG("metric %s: new bucket start time: %lld", mName.c_str(),
          (long long)mCurrentBucketStartTimeNs);
 }
 
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 4c49927..51af83d 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -19,6 +19,7 @@
 #include <gtest/gtest_prod.h>
 #include <utils/threads.h>
 #include <list>
+#include "../anomaly/AnomalyTracker.h"
 #include "../condition/ConditionTracker.h"
 #include "../external/PullDataReceiver.h"
 #include "../external/StatsPullerManager.h"
@@ -74,7 +75,7 @@
     // Util function to flush the old packet.
     void flushIfNeededLocked(const uint64_t& eventTime);
 
-    const ValueMetric mMetric;
+    const int32_t mValueField;
 
     std::shared_ptr<StatsPullerManager> mStatsPullerManager;
 
@@ -117,6 +118,7 @@
     FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
     FRIEND_TEST(ValueMetricProducerTest, TestEventsWithNonSlicedCondition);
     FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition);
+    FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 3a4dfda..9919abf 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -23,13 +23,14 @@
 #include <android-base/file.h>
 #include <dirent.h>
 
-using android::util::FIELD_COUNT_REPEATED;
-using android::util::FIELD_TYPE_MESSAGE;
-
 namespace android {
 namespace os {
 namespace statsd {
 
+using android::util::FIELD_COUNT_REPEATED;
+using android::util::FIELD_TYPE_MESSAGE;
+using std::map;
+
 #define STATS_SERVICE_DIR "/data/misc/stats-service"
 
 // for ConfigMetricsReportList
@@ -170,7 +171,7 @@
     }
 }
 
-void StorageManager::readConfigFromDisk(unordered_map<ConfigKey, StatsdConfig>& configsMap) {
+void StorageManager::readConfigFromDisk(map<ConfigKey, StatsdConfig>& configsMap) {
     unique_ptr<DIR, decltype(&closedir)> dir(opendir(STATS_SERVICE_DIR), closedir);
     if (dir == NULL) {
         VLOG("no default config on disk");
diff --git a/cmds/statsd/src/storage/StorageManager.h b/cmds/statsd/src/storage/StorageManager.h
index 4c9abe5..caf5b8b 100644
--- a/cmds/statsd/src/storage/StorageManager.h
+++ b/cmds/statsd/src/storage/StorageManager.h
@@ -66,7 +66,7 @@
     /**
      * Call to load the saved configs from disk.
      */
-    static void readConfigFromDisk(unordered_map<ConfigKey, StatsdConfig>& configsMap);
+    static void readConfigFromDisk(std::map<ConfigKey, StatsdConfig>& configsMap);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 146a19d..6f117d3 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -238,6 +238,79 @@
     EXPECT_EQ(30, valueProducer.mPastBuckets.begin()->second.back().mValue);
 }
 
+TEST(ValueMetricProducerTest, TestAnomalyDetection) {
+    Alert alert;
+    alert.set_name("alert");
+    alert.set_metric_name(metricName);
+    alert.set_trigger_if_sum_gt(130);
+    alert.set_number_of_buckets(2);
+    alert.set_refractory_period_secs(3);
+    sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, kConfigKey);
+
+    ValueMetric metric;
+    metric.set_name(metricName);
+    metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
+    metric.set_value_field(2);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      -1 /*not pulled*/, bucketStartTimeNs);
+    valueProducer.addAnomalyTracker(anomalyTracker);
+
+
+    shared_ptr<LogEvent> event1
+            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 1 * NS_PER_SEC);
+    event1->write(161);
+    event1->write(10); // value of interest
+    event1->init();
+    shared_ptr<LogEvent> event2
+            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 + NS_PER_SEC);
+    event2->write(162);
+    event2->write(20); // value of interest
+    event2->init();
+    shared_ptr<LogEvent> event3
+            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 1 * NS_PER_SEC);
+    event3->write(163);
+    event3->write(130); // value of interest
+    event3->init();
+    shared_ptr<LogEvent> event4
+            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 1 * NS_PER_SEC);
+    event4->write(35);
+    event4->write(1); // value of interest
+    event4->init();
+    shared_ptr<LogEvent> event5
+            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 2 * NS_PER_SEC);
+    event5->write(45);
+    event5->write(150); // value of interest
+    event5->init();
+    shared_ptr<LogEvent> event6
+            = make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10 * NS_PER_SEC);
+    event6->write(25);
+    event6->write(160); // value of interest
+    event6->init();
+
+    // Two events in bucket #0.
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
+    EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), -1LL); // Value sum == 30 <= 130.
+
+    // One event in bucket #2. No alarm as bucket #0 is trashed out.
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
+    EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), -1LL); // Value sum == 130 <= 130.
+
+    // Three events in bucket #3.
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
+    // Anomaly at event 4 since Value sum == 131 > 130!
+    EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event4->GetTimestampNs());
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event5);
+    // Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4.
+    EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event4->GetTimestampNs());
+
+    valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event6);
+    // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period.
+    EXPECT_EQ(anomalyTracker->getLastAlarmTimestampNs(), (long long)event6->GetTimestampNs());
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/tools/dogfood/res/layout/activity_main.xml b/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
index 3997897..5d35c29 100644
--- a/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
+++ b/cmds/statsd/tools/dogfood/res/layout/activity_main.xml
@@ -111,6 +111,23 @@
                 android:text="@string/screen_off"/>
         </LinearLayout>
 
+        <LinearLayout android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:orientation="horizontal">
+
+            <Button
+                android:id="@+id/custom_start"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/custom_start" />
+
+            <Button
+                android:id="@+id/custom_stop"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/custom_stop" />
+        </LinearLayout>
+
         <Button android:id="@+id/dump"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
diff --git a/cmds/statsd/tools/dogfood/res/values/strings.xml b/cmds/statsd/tools/dogfood/res/values/strings.xml
index 7690df6..0eab0f4 100644
--- a/cmds/statsd/tools/dogfood/res/values/strings.xml
+++ b/cmds/statsd/tools/dogfood/res/values/strings.xml
@@ -47,6 +47,9 @@
     <string name="screen_on">Screen On</string>
     <string name="screen_off">Screen Off</string>
 
+    <string name="custom_start">App hook start</string>
+    <string name="custom_stop">App hook stop</string>
+
     <string name="dump">DumpReport</string>
     <string name="report_header">Report details</string>
 </resources>
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
index c83802e..70dd634 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
@@ -140,6 +140,20 @@
             }
         });
 
+        findViewById(R.id.custom_start).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                StatsLog.logStart(8);
+            }
+        });
+
+        findViewById(R.id.custom_stop).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                StatsLog.logStop(8);
+            }
+        });
+
         mReportText = (TextView) findViewById(R.id.report_text);
 
         findViewById(R.id.dump).setOnClickListener(new View.OnClickListener() {
diff --git a/cmds/uiautomator/instrumentation/Android.mk b/cmds/uiautomator/instrumentation/Android.mk
index 008bb93..ed99f3e 100644
--- a/cmds/uiautomator/instrumentation/Android.mk
+++ b/cmds/uiautomator/instrumentation/Android.mk
@@ -21,7 +21,7 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := $(call all-java-files-under, testrunner-src) \
     $(call all-java-files-under, ../library/core-src)
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base.stubs
 LOCAL_STATIC_JAVA_LIBRARIES := junit
 LOCAL_MODULE := uiautomator-instrumentation
 # TODO: change this to 18 when it's available
diff --git a/cmds/wm/Android.mk b/cmds/wm/Android.mk
index 3f3795f..693c6e7 100644
--- a/cmds/wm/Android.mk
+++ b/cmds/wm/Android.mk
@@ -3,11 +3,6 @@
 LOCAL_PATH:= $(call my-dir)
 
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_MODULE := wm
-include $(BUILD_JAVA_LIBRARY)
-
-include $(CLEAR_VARS)
 LOCAL_MODULE := wm
 LOCAL_SRC_FILES := wm
 LOCAL_MODULE_CLASS := EXECUTABLES
diff --git a/cmds/wm/src/com/android/commands/wm/Wm.java b/cmds/wm/src/com/android/commands/wm/Wm.java
deleted file mode 100644
index 8defb33..0000000
--- a/cmds/wm/src/com/android/commands/wm/Wm.java
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
-**
-** Copyright 2013, 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.commands.wm;
-
-import android.content.Context;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.util.AndroidException;
-import android.util.DisplayMetrics;
-import android.system.Os;
-import android.view.Display;
-import android.view.IWindowManager;
-import com.android.internal.os.BaseCommand;
-
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.DataInputStream;
-import java.io.PrintStream;
-import java.lang.Runtime;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class Wm extends BaseCommand {
-
-    private IWindowManager mWm;
-
-    /**
-     * Command-line entry point.
-     *
-     * @param args The command-line arguments
-     */
-    public static void main(String[] args) {
-        (new Wm()).run(args);
-    }
-
-    @Override
-    public void onShowUsage(PrintStream out) {
-        out.println(
-                "usage: wm [subcommand] [options]\n" +
-                "       wm size [reset|WxH|WdpxHdp]\n" +
-                "       wm density [reset|DENSITY]\n" +
-                "       wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]\n" +
-                "       wm scaling [off|auto]\n" +
-                "       wm screen-capture [userId] [true|false]\n" +
-                "\n" +
-                "wm size: return or override display size.\n" +
-                "         width and height in pixels unless suffixed with 'dp'.\n" +
-                "\n" +
-                "wm density: override display density.\n" +
-                "\n" +
-                "wm overscan: set overscan area for display.\n" +
-                "\n" +
-                "wm scaling: set display scaling mode.\n" +
-                "\n" +
-                "wm screen-capture: enable/disable screen capture.\n" +
-                "\n" +
-                "wm dismiss-keyguard: dismiss the keyguard, prompting the user for auth if " +
-                "necessary.\n" +
-                "\n" +
-                "wm surface-trace: log surface commands to stdout in a binary format.\n"
-                );
-    }
-
-    @Override
-    public void onRun() throws Exception {
-        mWm = IWindowManager.Stub.asInterface(ServiceManager.checkService(
-                        Context.WINDOW_SERVICE));
-        if (mWm == null) {
-            System.err.println(NO_SYSTEM_ERROR_CODE);
-            throw new AndroidException("Can't connect to window manager; is the system running?");
-        }
-
-        String op = nextArgRequired();
-
-        if (op.equals("size")) {
-            runDisplaySize();
-        } else if (op.equals("density")) {
-            runDisplayDensity();
-        } else if (op.equals("overscan")) {
-            runDisplayOverscan();
-        } else if (op.equals("scaling")) {
-            runDisplayScaling();
-        } else if (op.equals("screen-capture")) {
-            runSetScreenCapture();
-        } else if (op.equals("dismiss-keyguard")) {
-            runDismissKeyguard();
-        } else if (op.equals("surface-trace")) {
-            runSurfaceTrace();
-        } else {
-            showError("Error: unknown command '" + op + "'");
-            return;
-        }
-    }
-
-    private void runSurfaceTrace() throws Exception {
-        ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(FileDescriptor.out);
-        mWm.enableSurfaceTrace(pfd);
-
-        try {
-            // No one is going to wake us up, we are just waiting on SIGINT. Otherwise
-            // the WM can happily continue writing to our stdout.
-            synchronized (this) {
-                this.wait();
-            }
-        } finally {
-            mWm.disableSurfaceTrace();
-        }
-    }
-
-    private void runSetScreenCapture() throws Exception {
-        String userIdStr = nextArg();
-        String enableStr = nextArg();
-        int userId;
-        boolean disable;
-
-        try {
-            userId = Integer.parseInt(userIdStr);
-        } catch (NumberFormatException e) {
-            System.err.println("Error: bad number " + e);
-            return;
-        }
-
-        disable = !Boolean.parseBoolean(enableStr);
-
-        try {
-            mWm.setScreenCaptureDisabled(userId, disable);
-        } catch (RemoteException e) {
-            System.err.println("Error: Can't set screen capture " + e);
-        }
-    }
-
-    private void runDisplaySize() throws Exception {
-        String size = nextArg();
-        int w, h;
-        if (size == null) {
-            Point initialSize = new Point();
-            Point baseSize = new Point();
-            try {
-                mWm.getInitialDisplaySize(Display.DEFAULT_DISPLAY, initialSize);
-                mWm.getBaseDisplaySize(Display.DEFAULT_DISPLAY, baseSize);
-                System.out.println("Physical size: " + initialSize.x + "x" + initialSize.y);
-                if (!initialSize.equals(baseSize)) {
-                    System.out.println("Override size: " + baseSize.x + "x" + baseSize.y);
-                }
-            } catch (RemoteException e) {
-            }
-            return;
-        } else if ("reset".equals(size)) {
-            w = h = -1;
-        } else {
-            int div = size.indexOf('x');
-            if (div <= 0 || div >= (size.length()-1)) {
-                System.err.println("Error: bad size " + size);
-                return;
-            }
-            String wstr = size.substring(0, div);
-            String hstr = size.substring(div+1);
-            try {
-                w = parseDimension(wstr);
-                h = parseDimension(hstr);
-            } catch (NumberFormatException e) {
-                System.err.println("Error: bad number " + e);
-                return;
-            }
-        }
-
-        try {
-            if (w >= 0 && h >= 0) {
-                // TODO(multidisplay): For now Configuration only applies to main screen.
-                mWm.setForcedDisplaySize(Display.DEFAULT_DISPLAY, w, h);
-            } else {
-                mWm.clearForcedDisplaySize(Display.DEFAULT_DISPLAY);
-            }
-        } catch (RemoteException e) {
-        }
-    }
-
-    private void runDisplayDensity() throws Exception {
-        String densityStr = nextArg();
-        int density;
-        if (densityStr == null) {
-            try {
-                int initialDensity = mWm.getInitialDisplayDensity(Display.DEFAULT_DISPLAY);
-                int baseDensity = mWm.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
-                System.out.println("Physical density: " + initialDensity);
-                if (initialDensity != baseDensity) {
-                    System.out.println("Override density: " + baseDensity);
-                }
-            } catch (RemoteException e) {
-            }
-            return;
-        } else if ("reset".equals(densityStr)) {
-            density = -1;
-        } else {
-            try {
-                density = Integer.parseInt(densityStr);
-            } catch (NumberFormatException e) {
-                System.err.println("Error: bad number " + e);
-                return;
-            }
-            if (density < 72) {
-                System.err.println("Error: density must be >= 72");
-                return;
-            }
-        }
-
-        try {
-            if (density > 0) {
-                // TODO(multidisplay): For now Configuration only applies to main screen.
-                mWm.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, density,
-                        UserHandle.USER_CURRENT);
-            } else {
-                mWm.clearForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY,
-                        UserHandle.USER_CURRENT);
-            }
-        } catch (RemoteException e) {
-        }
-    }
-
-    private void runDisplayOverscan() throws Exception {
-        String overscanStr = nextArgRequired();
-        Rect rect = new Rect();
-        if ("reset".equals(overscanStr)) {
-            rect.set(0, 0, 0, 0);
-        } else {
-            final Pattern FLATTENED_PATTERN = Pattern.compile(
-                    "(-?\\d+),(-?\\d+),(-?\\d+),(-?\\d+)");
-            Matcher matcher = FLATTENED_PATTERN.matcher(overscanStr);
-            if (!matcher.matches()) {
-                System.err.println("Error: bad rectangle arg: " + overscanStr);
-                return;
-            }
-            rect.left = Integer.parseInt(matcher.group(1));
-            rect.top = Integer.parseInt(matcher.group(2));
-            rect.right = Integer.parseInt(matcher.group(3));
-            rect.bottom = Integer.parseInt(matcher.group(4));
-        }
-
-        try {
-            mWm.setOverscan(Display.DEFAULT_DISPLAY, rect.left, rect.top, rect.right, rect.bottom);
-        } catch (RemoteException e) {
-        }
-    }
-
-    private void runDisplayScaling() throws Exception {
-        String scalingStr = nextArgRequired();
-        if ("auto".equals(scalingStr)) {
-            mWm.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 0);
-        } else if ("off".equals(scalingStr)) {
-            mWm.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1);
-        } else {
-            System.err.println("Error: scaling must be 'auto' or 'off'");
-        }
-    }
-
-    private void runDismissKeyguard() throws Exception {
-        mWm.dismissKeyguard(null /* callback */);
-    }
-
-    private int parseDimension(String s) throws NumberFormatException {
-        if (s.endsWith("px")) {
-            return Integer.parseInt(s.substring(0, s.length() - 2));
-        }
-        if (s.endsWith("dp")) {
-            int density;
-            try {
-                density = mWm.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
-            } catch (RemoteException e) {
-                density = DisplayMetrics.DENSITY_DEFAULT;
-            }
-            return Integer.parseInt(s.substring(0, s.length() - 2)) * density /
-                    DisplayMetrics.DENSITY_DEFAULT;
-        }
-        return Integer.parseInt(s);
-    }
-}
diff --git a/cmds/wm/wm b/cmds/wm/wm
index 16d6bd6..cb45be2 100755
--- a/cmds/wm/wm
+++ b/cmds/wm/wm
@@ -1,7 +1,2 @@
 #!/system/bin/sh
-# Script to start "wm" on the device, which has a very rudimentary
-# shell.
-#
-base=/system
-export CLASSPATH=$base/framework/wm.jar
-exec app_process $base/bin com.android.commands.wm.Wm "$@"
+cmd window "$@"
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 6666fcc..998d77f 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -309,4 +309,11 @@
      * Returns {@code true} if {@code uid} is running an activity from {@code packageName}.
      */
     public abstract boolean hasRunningActivity(int uid, @Nullable String packageName);
+
+    public interface ScreenObserver {
+        public void onAwakeStateChanged(boolean isAwake);
+        public void onKeyguardStateChanged(boolean isShowing);
+    }
+
+    public abstract void registerScreenObserver(ScreenObserver observer);
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ef446c2..da57efa 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -377,7 +377,7 @@
 
         ActivityInfo activityInfo;
         CompatibilityInfo compatInfo;
-        public LoadedApk packageInfo;
+        public LoadedApk loadedApk;
 
         List<ResultInfo> pendingResults;
         List<ReferrerIntent> pendingIntents;
@@ -420,7 +420,7 @@
             this.isForward = isForward;
             this.profilerInfo = profilerInfo;
             this.overrideConfig = overrideConfig;
-            this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo,
+            this.loadedApk = client.getLoadedApkNoCheck(activityInfo.applicationInfo,
                     compatInfo);
             init();
         }
@@ -602,7 +602,7 @@
     }
 
     static final class AppBindData {
-        LoadedApk info;
+        LoadedApk loadedApk;
         String processName;
         ApplicationInfo appInfo;
         List<ProviderInfo> providers;
@@ -1774,13 +1774,13 @@
         return mH;
     }
 
-    public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
-            int flags) {
-        return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId());
+    public final LoadedApk getLoadedApkForPackageName(String packageName,
+            CompatibilityInfo compatInfo, int flags) {
+        return getLoadedApkForPackageName(packageName, compatInfo, flags, UserHandle.myUserId());
     }
 
-    public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
-            int flags, int userId) {
+    public final LoadedApk getLoadedApkForPackageName(String packageName,
+            CompatibilityInfo compatInfo, int flags, int userId) {
         final boolean differentUser = (UserHandle.myUserId() != userId);
         synchronized (mResourcesManager) {
             WeakReference<LoadedApk> ref;
@@ -1793,13 +1793,13 @@
                 ref = mResourcePackages.get(packageName);
             }
 
-            LoadedApk packageInfo = ref != null ? ref.get() : null;
-            //Slog.i(TAG, "getPackageInfo " + packageName + ": " + packageInfo);
-            //if (packageInfo != null) Slog.i(TAG, "isUptoDate " + packageInfo.mResDir
-            //        + ": " + packageInfo.mResources.getAssets().isUpToDate());
-            if (packageInfo != null && (packageInfo.mResources == null
-                    || packageInfo.mResources.getAssets().isUpToDate())) {
-                if (packageInfo.isSecurityViolation()
+            LoadedApk loadedApk = ref != null ? ref.get() : null;
+            //Slog.i(TAG, "getLoadedApkForPackageName " + packageName + ": " + loadedApk);
+            //if (loadedApk != null) Slog.i(TAG, "isUptoDate " + loadedApk.mResDir
+            //        + ": " + loadedApk.mResources.getAssets().isUpToDate());
+            if (loadedApk != null && (loadedApk.mResources == null
+                    || loadedApk.mResources.getAssets().isUpToDate())) {
+                if (loadedApk.isSecurityViolation()
                         && (flags&Context.CONTEXT_IGNORE_SECURITY) == 0) {
                     throw new SecurityException(
                             "Requesting code from " + packageName
@@ -1807,7 +1807,7 @@
                             + mBoundApplication.processName
                             + "/" + mBoundApplication.appInfo.uid);
                 }
-                return packageInfo;
+                return loadedApk;
             }
         }
 
@@ -1822,13 +1822,13 @@
         }
 
         if (ai != null) {
-            return getPackageInfo(ai, compatInfo, flags);
+            return getLoadedApk(ai, compatInfo, flags);
         }
 
         return null;
     }
 
-    public final LoadedApk getPackageInfo(ApplicationInfo ai, CompatibilityInfo compatInfo,
+    public final LoadedApk getLoadedApk(ApplicationInfo ai, CompatibilityInfo compatInfo,
             int flags) {
         boolean includeCode = (flags&Context.CONTEXT_INCLUDE_CODE) != 0;
         boolean securityViolation = includeCode && ai.uid != 0
@@ -1850,17 +1850,17 @@
                 throw new SecurityException(msg);
             }
         }
-        return getPackageInfo(ai, compatInfo, null, securityViolation, includeCode,
+        return getLoadedApk(ai, compatInfo, null, securityViolation, includeCode,
                 registerPackage);
     }
 
     @Override
-    public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
+    public final LoadedApk getLoadedApkNoCheck(ApplicationInfo ai,
             CompatibilityInfo compatInfo) {
-        return getPackageInfo(ai, compatInfo, null, false, true, false);
+        return getLoadedApk(ai, compatInfo, null, false, true, false);
     }
 
-    public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) {
+    public final LoadedApk peekLoadedApk(String packageName, boolean includeCode) {
         synchronized (mResourcesManager) {
             WeakReference<LoadedApk> ref;
             if (includeCode) {
@@ -1872,7 +1872,7 @@
         }
     }
 
-    private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
+    private LoadedApk getLoadedApk(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
             ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
             boolean registerPackage) {
         final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
@@ -1887,35 +1887,35 @@
                 ref = mResourcePackages.get(aInfo.packageName);
             }
 
-            LoadedApk packageInfo = ref != null ? ref.get() : null;
-            if (packageInfo == null || (packageInfo.mResources != null
-                    && !packageInfo.mResources.getAssets().isUpToDate())) {
+            LoadedApk loadedApk = ref != null ? ref.get() : null;
+            if (loadedApk == null || (loadedApk.mResources != null
+                    && !loadedApk.mResources.getAssets().isUpToDate())) {
                 if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
                         : "Loading resource-only package ") + aInfo.packageName
                         + " (in " + (mBoundApplication != null
                                 ? mBoundApplication.processName : null)
                         + ")");
-                packageInfo =
+                loadedApk =
                     new LoadedApk(this, aInfo, compatInfo, baseLoader,
                             securityViolation, includeCode &&
                             (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);
 
                 if (mSystemThread && "android".equals(aInfo.packageName)) {
-                    packageInfo.installSystemApplicationInfo(aInfo,
-                            getSystemContext().mPackageInfo.getClassLoader());
+                    loadedApk.installSystemApplicationInfo(aInfo,
+                            getSystemContext().mLoadedApk.getClassLoader());
                 }
 
                 if (differentUser) {
                     // Caching not supported across users
                 } else if (includeCode) {
                     mPackages.put(aInfo.packageName,
-                            new WeakReference<LoadedApk>(packageInfo));
+                            new WeakReference<LoadedApk>(loadedApk));
                 } else {
                     mResourcePackages.put(aInfo.packageName,
-                            new WeakReference<LoadedApk>(packageInfo));
+                            new WeakReference<LoadedApk>(loadedApk));
                 }
             }
-            return packageInfo;
+            return loadedApk;
         }
     }
 
@@ -2627,8 +2627,8 @@
     /**  Core implementation of activity launch. */
     private Activity performLaunchActivity(ActivityClientRecord r) {
         ActivityInfo aInfo = r.activityInfo;
-        if (r.packageInfo == null) {
-            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
+        if (r.loadedApk == null) {
+            r.loadedApk = getLoadedApk(aInfo.applicationInfo, r.compatInfo,
                     Context.CONTEXT_INCLUDE_CODE);
         }
 
@@ -2665,15 +2665,15 @@
         }
 
         try {
-            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
+            Application app = r.loadedApk.makeApplication(false, mInstrumentation);
 
             if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
             if (localLOGV) Slog.v(
                     TAG, r + ": app=" + app
                     + ", appName=" + app.getPackageName()
-                    + ", pkg=" + r.packageInfo.getPackageName()
+                    + ", pkg=" + r.loadedApk.getPackageName()
                     + ", comp=" + r.intent.getComponent().toShortString()
-                    + ", dir=" + r.packageInfo.getAppDir());
+                    + ", dir=" + r.loadedApk.getAppDir());
 
             if (activity != null) {
                 CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
@@ -2813,7 +2813,7 @@
         }
 
         ContextImpl appContext = ContextImpl.createActivityContext(
-                this, r.packageInfo, r.activityInfo, r.token, displayId, r.overrideConfig);
+                this, r.loadedApk, r.activityInfo, r.token, displayId, r.overrideConfig);
 
         final DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
         // For debugging purposes, if the activity's package name contains the value of
@@ -2821,7 +2821,7 @@
         // its content on a secondary display if there is one.
         String pkgName = SystemProperties.get("debug.second-display.pkg");
         if (pkgName != null && !pkgName.isEmpty()
-                && r.packageInfo.mPackageName.contains(pkgName)) {
+                && r.loadedApk.mPackageName.contains(pkgName)) {
             for (int id : dm.getDisplayIds()) {
                 if (id != Display.DEFAULT_DISPLAY) {
                     Display display =
@@ -3141,7 +3141,7 @@
 
         String component = data.intent.getComponent().getClassName();
 
-        LoadedApk packageInfo = getPackageInfoNoCheck(
+        LoadedApk loadedApk = getLoadedApkNoCheck(
                 data.info.applicationInfo, data.compatInfo);
 
         IActivityManager mgr = ActivityManager.getService();
@@ -3150,7 +3150,7 @@
         BroadcastReceiver receiver;
         ContextImpl context;
         try {
-            app = packageInfo.makeApplication(false, mInstrumentation);
+            app = loadedApk.makeApplication(false, mInstrumentation);
             context = (ContextImpl) app.getBaseContext();
             if (data.info.splitName != null) {
                 context = (ContextImpl) context.createContextForSplit(data.info.splitName);
@@ -3159,7 +3159,7 @@
             data.intent.setExtrasClassLoader(cl);
             data.intent.prepareToEnterProcess();
             data.setExtrasClassLoader(cl);
-            receiver = packageInfo.getAppFactory()
+            receiver = loadedApk.getAppFactory()
                     .instantiateReceiver(cl, data.info.name, data.intent);
         } catch (Exception e) {
             if (DEBUG_BROADCAST) Slog.i(TAG,
@@ -3175,9 +3175,9 @@
                 TAG, "Performing receive of " + data.intent
                 + ": app=" + app
                 + ", appName=" + app.getPackageName()
-                + ", pkg=" + packageInfo.getPackageName()
+                + ", pkg=" + loadedApk.getPackageName()
                 + ", comp=" + data.intent.getComponent().toShortString()
-                + ", dir=" + packageInfo.getAppDir());
+                + ", dir=" + loadedApk.getAppDir());
 
             sCurrentBroadcastIntent.set(data.intent);
             receiver.setPendingResult(data);
@@ -3222,8 +3222,8 @@
         unscheduleGcIdler();
 
         // instantiate the BackupAgent class named in the manifest
-        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
-        String packageName = packageInfo.mPackageName;
+        LoadedApk loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
+        String packageName = loadedApk.mPackageName;
         if (packageName == null) {
             Slog.d(TAG, "Asked to create backup agent for nonexistent package");
             return;
@@ -3249,11 +3249,11 @@
                 try {
                     if (DEBUG_BACKUP) Slog.v(TAG, "Initializing agent class " + classname);
 
-                    java.lang.ClassLoader cl = packageInfo.getClassLoader();
+                    java.lang.ClassLoader cl = loadedApk.getClassLoader();
                     agent = (BackupAgent) cl.loadClass(classname).newInstance();
 
                     // set up the agent's context
-                    ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
+                    ContextImpl context = ContextImpl.createAppContext(this, loadedApk);
                     context.setOuterContext(agent);
                     agent.attach(context);
 
@@ -3289,8 +3289,8 @@
     private void handleDestroyBackupAgent(CreateBackupAgentData data) {
         if (DEBUG_BACKUP) Slog.v(TAG, "handleDestroyBackupAgent: " + data);
 
-        LoadedApk packageInfo = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
-        String packageName = packageInfo.mPackageName;
+        LoadedApk loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
+        String packageName = loadedApk.mPackageName;
         BackupAgent agent = mBackupAgents.get(packageName);
         if (agent != null) {
             try {
@@ -3310,12 +3310,12 @@
         // we are back active so skip it.
         unscheduleGcIdler();
 
-        LoadedApk packageInfo = getPackageInfoNoCheck(
+        LoadedApk loadedApk = getLoadedApkNoCheck(
                 data.info.applicationInfo, data.compatInfo);
         Service service = null;
         try {
-            java.lang.ClassLoader cl = packageInfo.getClassLoader();
-            service = packageInfo.getAppFactory()
+            java.lang.ClassLoader cl = loadedApk.getClassLoader();
+            service = loadedApk.getAppFactory()
                     .instantiateService(cl, data.info.name, data.intent);
         } catch (Exception e) {
             if (!mInstrumentation.onException(service, e)) {
@@ -3328,10 +3328,10 @@
         try {
             if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
 
-            ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
+            ContextImpl context = ContextImpl.createAppContext(this, loadedApk);
             context.setOuterContext(service);
 
-            Application app = packageInfo.makeApplication(false, mInstrumentation);
+            Application app = loadedApk.makeApplication(false, mInstrumentation);
             service.attach(context, this, data.info.name, data.token, app,
                     ActivityManager.getService());
             service.onCreate();
@@ -4195,11 +4195,11 @@
     }
 
     private void handleUpdatePackageCompatibilityInfo(UpdateCompatibilityData data) {
-        LoadedApk apk = peekPackageInfo(data.pkg, false);
+        LoadedApk apk = peekLoadedApk(data.pkg, false);
         if (apk != null) {
             apk.setCompatibilityInfo(data.info);
         }
-        apk = peekPackageInfo(data.pkg, true);
+        apk = peekLoadedApk(data.pkg, true);
         if (apk != null) {
             apk.setCompatibilityInfo(data.info);
         }
@@ -4693,7 +4693,7 @@
                 if (a != null) {
                     Configuration thisConfig = applyConfigCompatMainThread(
                             mCurDefaultDisplayDpi, newConfig,
-                            ar.packageInfo.getCompatibilityInfo());
+                            ar.loadedApk.getCompatibilityInfo());
                     if (!ar.activity.mFinished && (allActivities || !ar.paused)) {
                         // If the activity is currently resumed, its configuration
                         // needs to change right now.
@@ -5179,7 +5179,7 @@
     }
 
     final void handleDispatchPackageBroadcast(int cmd, String[] packages) {
-        boolean hasPkgInfo = false;
+        boolean hasLoadedApk = false;
         switch (cmd) {
             case ApplicationThreadConstants.PACKAGE_REMOVED:
             case ApplicationThreadConstants.PACKAGE_REMOVED_DONT_KILL:
@@ -5190,14 +5190,14 @@
                 }
                 synchronized (mResourcesManager) {
                     for (int i = packages.length - 1; i >= 0; i--) {
-                        if (!hasPkgInfo) {
+                        if (!hasLoadedApk) {
                             WeakReference<LoadedApk> ref = mPackages.get(packages[i]);
                             if (ref != null && ref.get() != null) {
-                                hasPkgInfo = true;
+                                hasLoadedApk = true;
                             } else {
                                 ref = mResourcePackages.get(packages[i]);
                                 if (ref != null && ref.get() != null) {
-                                    hasPkgInfo = true;
+                                    hasLoadedApk = true;
                                 }
                             }
                         }
@@ -5217,21 +5217,21 @@
                 synchronized (mResourcesManager) {
                     for (int i = packages.length - 1; i >= 0; i--) {
                         WeakReference<LoadedApk> ref = mPackages.get(packages[i]);
-                        LoadedApk pkgInfo = ref != null ? ref.get() : null;
-                        if (pkgInfo != null) {
-                            hasPkgInfo = true;
+                        LoadedApk loadedApk = ref != null ? ref.get() : null;
+                        if (loadedApk != null) {
+                            hasLoadedApk = true;
                         } else {
                             ref = mResourcePackages.get(packages[i]);
-                            pkgInfo = ref != null ? ref.get() : null;
-                            if (pkgInfo != null) {
-                                hasPkgInfo = true;
+                            loadedApk = ref != null ? ref.get() : null;
+                            if (loadedApk != null) {
+                                hasLoadedApk = true;
                             }
                         }
                         // If the package is being replaced, yet it still has a valid
                         // LoadedApk object, the package was updated with _DONT_KILL.
                         // Adjust it's internal references to the application info and
                         // resources.
-                        if (pkgInfo != null) {
+                        if (loadedApk != null) {
                             try {
                                 final String packageName = packages[i];
                                 final ApplicationInfo aInfo =
@@ -5245,13 +5245,13 @@
                                         if (ar.activityInfo.applicationInfo.packageName
                                                 .equals(packageName)) {
                                             ar.activityInfo.applicationInfo = aInfo;
-                                            ar.packageInfo = pkgInfo;
+                                            ar.loadedApk = loadedApk;
                                         }
                                     }
                                 }
                                 final List<String> oldPaths =
                                         sPackageManager.getPreviousCodePaths(packageName);
-                                pkgInfo.updateApplicationInfo(aInfo, oldPaths);
+                                loadedApk.updateApplicationInfo(aInfo, oldPaths);
                             } catch (RemoteException e) {
                             }
                         }
@@ -5260,7 +5260,7 @@
                 break;
             }
         }
-        ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasPkgInfo);
+        ApplicationPackageManager.handlePackageBroadcast(cmd, packages, hasLoadedApk);
     }
 
     final void handleLowMemory() {
@@ -5464,7 +5464,7 @@
             applyCompatConfiguration(mCurDefaultDisplayDpi);
         }
 
-        data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
+        data.loadedApk = getLoadedApkNoCheck(data.appInfo, data.compatInfo);
 
         /**
          * Switch this process to density compatibility mode if needed.
@@ -5508,7 +5508,7 @@
             // XXX should have option to change the port.
             Debug.changeDebugPort(8100);
             if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) {
-                Slog.w(TAG, "Application " + data.info.getPackageName()
+                Slog.w(TAG, "Application " + data.loadedApk.getPackageName()
                       + " is waiting for the debugger on port 8100...");
 
                 IActivityManager mgr = ActivityManager.getService();
@@ -5527,7 +5527,7 @@
                 }
 
             } else {
-                Slog.w(TAG, "Application " + data.info.getPackageName()
+                Slog.w(TAG, "Application " + data.loadedApk.getPackageName()
                       + " can be debugged on port 8100...");
             }
         }
@@ -5575,14 +5575,14 @@
             mInstrumentationAppDir = ii.sourceDir;
             mInstrumentationSplitAppDirs = ii.splitSourceDirs;
             mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii);
-            mInstrumentedAppDir = data.info.getAppDir();
-            mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
-            mInstrumentedLibDir = data.info.getLibDir();
+            mInstrumentedAppDir = data.loadedApk.getAppDir();
+            mInstrumentedSplitAppDirs = data.loadedApk.getSplitAppDirs();
+            mInstrumentedLibDir = data.loadedApk.getLibDir();
         } else {
             ii = null;
         }
 
-        final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
+        final ContextImpl appContext = ContextImpl.createAppContext(this, data.loadedApk);
         updateLocaleListFromAppContext(appContext,
                 mResourcesManager.getConfiguration().getLocales());
 
@@ -5626,9 +5626,9 @@
             }
             ii.copyTo(instrApp);
             instrApp.initForUser(UserHandle.myUserId());
-            final LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
+            final LoadedApk loadedApk = getLoadedApk(instrApp, data.compatInfo,
                     appContext.getClassLoader(), false, true, false);
-            final ContextImpl instrContext = ContextImpl.createAppContext(this, pi);
+            final ContextImpl instrContext = ContextImpl.createAppContext(this, loadedApk);
 
             try {
                 final ClassLoader cl = instrContext.getClassLoader();
@@ -5673,7 +5673,7 @@
         try {
             // If the app is being launched for full backup or restore, bring it up in
             // a restricted environment with the base application class.
-            app = data.info.makeApplication(data.restrictedBackupMode, null);
+            app = data.loadedApk.makeApplication(data.restrictedBackupMode, null);
             mInitialApplication = app;
 
             // don't bring up providers in restricted mode; they may depend on the
@@ -5727,7 +5727,7 @@
                 final int preloadedFontsResource = info.metaData.getInt(
                         ApplicationInfo.METADATA_PRELOADED_FONTS, 0);
                 if (preloadedFontsResource != 0) {
-                    data.info.getResources().preloadFonts(preloadedFontsResource);
+                    data.loadedApk.getResources().preloadFonts(preloadedFontsResource);
                 }
             }
         } catch (RemoteException e) {
@@ -6185,12 +6185,12 @@
 
             try {
                 final java.lang.ClassLoader cl = c.getClassLoader();
-                LoadedApk packageInfo = peekPackageInfo(ai.packageName, true);
-                if (packageInfo == null) {
+                LoadedApk loadedApk = peekLoadedApk(ai.packageName, true);
+                if (loadedApk == null) {
                     // System startup case.
-                    packageInfo = getSystemContext().mPackageInfo;
+                    loadedApk = getSystemContext().mLoadedApk;
                 }
-                localProvider = packageInfo.getAppFactory()
+                localProvider = loadedApk.getAppFactory()
                         .instantiateProvider(cl, info.name);
                 provider = localProvider.getIContentProvider();
                 if (provider == null) {
@@ -6339,8 +6339,8 @@
                 mInstrumentation = new Instrumentation();
                 mInstrumentation.basicInit(this);
                 ContextImpl context = ContextImpl.createAppContext(
-                        this, getSystemContext().mPackageInfo);
-                mInitialApplication = context.mPackageInfo.makeApplication(true, null);
+                        this, getSystemContext().mLoadedApk);
+                mInitialApplication = context.mLoadedApk.makeApplication(true, null);
                 mInitialApplication.onCreate();
             } catch (Exception e) {
                 throw new RuntimeException(
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index c943574..50e3f0a 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -160,7 +160,7 @@
     public static final int OP_WRITE_ICC_SMS = 22;
     /** @hide */
     public static final int OP_WRITE_SETTINGS = 23;
-    /** @hide */
+    /** @hide Required to draw on top of other apps. */
     public static final int OP_SYSTEM_ALERT_WINDOW = 24;
     /** @hide */
     public static final int OP_ACCESS_NOTIFICATIONS = 25;
@@ -260,8 +260,10 @@
     public static final int OP_REQUEST_DELETE_PACKAGES = 72;
     /** @hide Bind an accessibility service. */
     public static final int OP_BIND_ACCESSIBILITY_SERVICE = 73;
+    /** @hide Interact with the system UI via an Accessibility Service */
+    public static final int OP_PERFORM_ACCESSIBILITY_ACTION = 74;
     /** @hide */
-    public static final int _NUM_OP = 74;
+    public static final int _NUM_OP = 75;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
@@ -506,6 +508,7 @@
             OP_CHANGE_WIFI_STATE,
             OP_REQUEST_DELETE_PACKAGES,
             OP_BIND_ACCESSIBILITY_SERVICE,
+            OP_PERFORM_ACCESSIBILITY_ACTION,
     };
 
     /**
@@ -587,6 +590,7 @@
             null, // OP_CHANGE_WIFI_STATE
             null, // OP_REQUEST_DELETE_PACKAGES
             null, // OP_BIND_ACCESSIBILITY_SERVICE
+            null, // OP_PERFORM_ACCESSIBILITY_ACTION
     };
 
     /**
@@ -668,6 +672,7 @@
             "CHANGE_WIFI_STATE",
             "REQUEST_DELETE_PACKAGES",
             "BIND_ACCESSIBILITY_SERVICE",
+            "OP_PERFORM_ACCESSIBILITY_ACTION",
     };
 
     /**
@@ -749,6 +754,7 @@
             Manifest.permission.CHANGE_WIFI_STATE,
             Manifest.permission.REQUEST_DELETE_PACKAGES,
             Manifest.permission.BIND_ACCESSIBILITY_SERVICE,
+            null, // no permission for OP_PERFORM_ACCESSIBILITY_ACTION
     };
 
     /**
@@ -831,6 +837,7 @@
             null, // OP_CHANGE_WIFI_STATE
             null, // REQUEST_DELETE_PACKAGES
             null, // OP_BIND_ACCESSIBILITY_SERVICE
+            null, // OP_PERFORM_ACCESSIBILITY_ACTION
     };
 
     /**
@@ -912,6 +919,7 @@
             false, // OP_CHANGE_WIFI_STATE
             false, // OP_REQUEST_DELETE_PACKAGES
             false, // OP_BIND_ACCESSIBILITY_SERVICE
+            false, // OP_PERFORM_ACCESSIBILITY_ACTION
     };
 
     /**
@@ -992,6 +1000,7 @@
             AppOpsManager.MODE_ALLOWED,  // OP_CHANGE_WIFI_STATE
             AppOpsManager.MODE_ALLOWED,  // REQUEST_DELETE_PACKAGES
             AppOpsManager.MODE_ALLOWED,  // OP_BIND_ACCESSIBILITY_SERVICE
+            AppOpsManager.MODE_ALLOWED,  // OP_PERFORM_ACCESSIBILITY_ACTION
     };
 
     /**
@@ -1076,6 +1085,7 @@
             false, // OP_CHANGE_WIFI_STATE
             false, // OP_REQUEST_DELETE_PACKAGES
             false, // OP_BIND_ACCESSIBILITY_SERVICE
+            false, // OP_PERFORM_ACCESSIBILITY_ACTION
     };
 
     /**
diff --git a/core/java/android/app/Application.java b/core/java/android/app/Application.java
index 156df36..5822f5c 100644
--- a/core/java/android/app/Application.java
+++ b/core/java/android/app/Application.java
@@ -187,7 +187,7 @@
      */
     /* package */ final void attach(Context context) {
         attachBaseContext(context);
-        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
+        mLoadedApk = ContextImpl.getImpl(context).mLoadedApk;
     }
 
     /* package */ void dispatchActivityCreated(Activity activity, Bundle savedInstanceState) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 1dbdb59..8641a21 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1381,7 +1381,7 @@
                     sameUid ? app.sourceDir : app.publicSourceDir,
                     sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
                     app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
-                    mContext.mPackageInfo);
+                    mContext.mLoadedApk);
         if (r != null) {
             return r;
         }
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index ef66af0..45c0e0c 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -110,7 +110,7 @@
             PendingTransactionActions pendingActions);
 
     /** Get package info. */
-    public abstract LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
+    public abstract LoadedApk getLoadedApkNoCheck(ApplicationInfo ai,
             CompatibilityInfo compatInfo);
 
     /** Deliver app configuration change notification. */
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index a2de0f4..1653430 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -159,7 +159,7 @@
     private ArrayMap<String, File> mSharedPrefsPaths;
 
     final @NonNull ActivityThread mMainThread;
-    final @NonNull LoadedApk mPackageInfo;
+    final @NonNull LoadedApk mLoadedApk;
     private @Nullable ClassLoader mClassLoader;
 
     private final @Nullable IBinder mActivityToken;
@@ -257,8 +257,8 @@
 
     @Override
     public Context getApplicationContext() {
-        return (mPackageInfo != null) ?
-                mPackageInfo.getApplication() : mMainThread.getApplication();
+        return (mLoadedApk != null) ?
+                mLoadedApk.getApplication() : mMainThread.getApplication();
     }
 
     @Override
@@ -302,15 +302,15 @@
 
     @Override
     public ClassLoader getClassLoader() {
-        return mClassLoader != null ? mClassLoader : (mPackageInfo != null ? mPackageInfo.getClassLoader() : ClassLoader.getSystemClassLoader());
+        return mClassLoader != null ? mClassLoader : (mLoadedApk != null ? mLoadedApk.getClassLoader() : ClassLoader.getSystemClassLoader());
     }
 
     @Override
     public String getPackageName() {
-        if (mPackageInfo != null) {
-            return mPackageInfo.getPackageName();
+        if (mLoadedApk != null) {
+            return mLoadedApk.getPackageName();
         }
-        // No mPackageInfo means this is a Context for the system itself,
+        // No mLoadedApk means this is a Context for the system itself,
         // and this here is its name.
         return "android";
     }
@@ -329,24 +329,24 @@
 
     @Override
     public ApplicationInfo getApplicationInfo() {
-        if (mPackageInfo != null) {
-            return mPackageInfo.getApplicationInfo();
+        if (mLoadedApk != null) {
+            return mLoadedApk.getApplicationInfo();
         }
         throw new RuntimeException("Not supported in system context");
     }
 
     @Override
     public String getPackageResourcePath() {
-        if (mPackageInfo != null) {
-            return mPackageInfo.getResDir();
+        if (mLoadedApk != null) {
+            return mLoadedApk.getResDir();
         }
         throw new RuntimeException("Not supported in system context");
     }
 
     @Override
     public String getPackageCodePath() {
-        if (mPackageInfo != null) {
-            return mPackageInfo.getAppDir();
+        if (mLoadedApk != null) {
+            return mLoadedApk.getAppDir();
         }
         throw new RuntimeException("Not supported in system context");
     }
@@ -356,7 +356,7 @@
         // At least one application in the world actually passes in a null
         // name.  This happened to work because when we generated the file name
         // we would stringify it to "null.xml".  Nice.
-        if (mPackageInfo.getApplicationInfo().targetSdkVersion <
+        if (mLoadedApk.getApplicationInfo().targetSdkVersion <
                 Build.VERSION_CODES.KITKAT) {
             if (name == null) {
                 name = "null";
@@ -1098,11 +1098,11 @@
         warnIfCallingFromSystemProcess();
         IIntentReceiver rd = null;
         if (resultReceiver != null) {
-            if (mPackageInfo != null) {
+            if (mLoadedApk != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mPackageInfo.getReceiverDispatcher(
+                rd = mLoadedApk.getReceiverDispatcher(
                     resultReceiver, getOuterContext(), scheduler,
                     mMainThread.getInstrumentation(), false);
             } else {
@@ -1202,11 +1202,11 @@
             Handler scheduler, int initialCode, String initialData, Bundle initialExtras) {
         IIntentReceiver rd = null;
         if (resultReceiver != null) {
-            if (mPackageInfo != null) {
+            if (mLoadedApk != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mPackageInfo.getReceiverDispatcher(
+                rd = mLoadedApk.getReceiverDispatcher(
                     resultReceiver, getOuterContext(), scheduler,
                     mMainThread.getInstrumentation(), false);
             } else {
@@ -1256,11 +1256,11 @@
         warnIfCallingFromSystemProcess();
         IIntentReceiver rd = null;
         if (resultReceiver != null) {
-            if (mPackageInfo != null) {
+            if (mLoadedApk != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mPackageInfo.getReceiverDispatcher(
+                rd = mLoadedApk.getReceiverDispatcher(
                     resultReceiver, getOuterContext(), scheduler,
                     mMainThread.getInstrumentation(), false);
             } else {
@@ -1338,11 +1338,11 @@
             Bundle initialExtras) {
         IIntentReceiver rd = null;
         if (resultReceiver != null) {
-            if (mPackageInfo != null) {
+            if (mLoadedApk != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mPackageInfo.getReceiverDispatcher(
+                rd = mLoadedApk.getReceiverDispatcher(
                     resultReceiver, getOuterContext(), scheduler,
                     mMainThread.getInstrumentation(), false);
             } else {
@@ -1419,11 +1419,11 @@
             Handler scheduler, Context context, int flags) {
         IIntentReceiver rd = null;
         if (receiver != null) {
-            if (mPackageInfo != null && context != null) {
+            if (mLoadedApk != null && context != null) {
                 if (scheduler == null) {
                     scheduler = mMainThread.getHandler();
                 }
-                rd = mPackageInfo.getReceiverDispatcher(
+                rd = mLoadedApk.getReceiverDispatcher(
                     receiver, context, scheduler,
                     mMainThread.getInstrumentation(), true);
             } else {
@@ -1450,8 +1450,8 @@
 
     @Override
     public void unregisterReceiver(BroadcastReceiver receiver) {
-        if (mPackageInfo != null) {
-            IIntentReceiver rd = mPackageInfo.forgetReceiverDispatcher(
+        if (mLoadedApk != null) {
+            IIntentReceiver rd = mLoadedApk.forgetReceiverDispatcher(
                     getOuterContext(), receiver);
             try {
                 ActivityManager.getService().unregisterReceiver(rd);
@@ -1584,7 +1584,7 @@
     @Override
     public IServiceConnection getServiceDispatcher(ServiceConnection conn, Handler handler,
             int flags) {
-        return mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
+        return mLoadedApk.getServiceDispatcher(conn, getOuterContext(), handler, flags);
     }
 
     /** @hide */
@@ -1606,16 +1606,16 @@
         if (conn == null) {
             throw new IllegalArgumentException("connection is null");
         }
-        if (mPackageInfo != null) {
-            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
+        if (mLoadedApk != null) {
+            sd = mLoadedApk.getServiceDispatcher(conn, getOuterContext(), handler, flags);
         } else {
             throw new RuntimeException("Not supported in system context");
         }
         validateServiceIntent(service);
         try {
             IBinder token = getActivityToken();
-            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null
-                    && mPackageInfo.getApplicationInfo().targetSdkVersion
+            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mLoadedApk != null
+                    && mLoadedApk.getApplicationInfo().targetSdkVersion
                     < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                 flags |= BIND_WAIVE_PRIORITY;
             }
@@ -1639,8 +1639,8 @@
         if (conn == null) {
             throw new IllegalArgumentException("connection is null");
         }
-        if (mPackageInfo != null) {
-            IServiceConnection sd = mPackageInfo.forgetServiceDispatcher(
+        if (mLoadedApk != null) {
+            IServiceConnection sd = mLoadedApk.forgetServiceDispatcher(
                     getOuterContext(), conn);
             try {
                 ActivityManager.getService().unbindService(sd);
@@ -1985,40 +1985,20 @@
         }
     }
 
-    private static Resources createResources(IBinder activityToken, LoadedApk pi, String splitName,
-            int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
-        final String[] splitResDirs;
-        final ClassLoader classLoader;
-        try {
-            splitResDirs = pi.getSplitPaths(splitName);
-            classLoader = pi.getSplitClassLoader(splitName);
-        } catch (NameNotFoundException e) {
-            throw new RuntimeException(e);
-        }
-        return ResourcesManager.getInstance().getResources(activityToken,
-                pi.getResDir(),
-                splitResDirs,
-                pi.getOverlayDirs(),
-                pi.getApplicationInfo().sharedLibraryFiles,
-                displayId,
-                overrideConfig,
-                compatInfo,
-                classLoader);
-    }
-
     @Override
     public Context createApplicationContext(ApplicationInfo application, int flags)
             throws NameNotFoundException {
-        LoadedApk pi = mMainThread.getPackageInfo(application, mResources.getCompatibilityInfo(),
+        LoadedApk loadedApk = mMainThread.getLoadedApk(application,
+                mResources.getCompatibilityInfo(),
                 flags | CONTEXT_REGISTER_PACKAGE);
-        if (pi != null) {
-            ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken,
+        if (loadedApk != null) {
+            ContextImpl c = new ContextImpl(this, mMainThread, loadedApk, null, mActivityToken,
                     new UserHandle(UserHandle.getUserId(application.uid)), flags, null);
 
             final int displayId = mDisplay != null
                     ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
 
-            c.setResources(createResources(mActivityToken, pi, null, displayId, null,
+            c.setResources(loadedApk.createResources(mActivityToken, null, displayId, null,
                     getDisplayAdjustments(displayId).getCompatibilityInfo()));
             if (c.mResources != null) {
                 return c;
@@ -2042,20 +2022,21 @@
         if (packageName.equals("system") || packageName.equals("android")) {
             // The system resources are loaded in every application, so we can safely copy
             // the context without reloading Resources.
-            return new ContextImpl(this, mMainThread, mPackageInfo, null, mActivityToken, user,
+            return new ContextImpl(this, mMainThread, mLoadedApk, null, mActivityToken, user,
                     flags, null);
         }
 
-        LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
+        LoadedApk loadedApk = mMainThread.getLoadedApkForPackageName(packageName,
+                mResources.getCompatibilityInfo(),
                 flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
-        if (pi != null) {
-            ContextImpl c = new ContextImpl(this, mMainThread, pi, null, mActivityToken, user,
+        if (loadedApk != null) {
+            ContextImpl c = new ContextImpl(this, mMainThread, loadedApk, null, mActivityToken, user,
                     flags, null);
 
             final int displayId = mDisplay != null
                     ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
 
-            c.setResources(createResources(mActivityToken, pi, null, displayId, null,
+            c.setResources(loadedApk.createResources(mActivityToken, null, displayId, null,
                     getDisplayAdjustments(displayId).getCompatibilityInfo()));
             if (c.mResources != null) {
                 return c;
@@ -2069,30 +2050,21 @@
 
     @Override
     public Context createContextForSplit(String splitName) throws NameNotFoundException {
-        if (!mPackageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
+        if (!mLoadedApk.getApplicationInfo().requestsIsolatedSplitLoading()) {
             // All Splits are always loaded.
             return this;
         }
 
-        final ClassLoader classLoader = mPackageInfo.getSplitClassLoader(splitName);
-        final String[] paths = mPackageInfo.getSplitPaths(splitName);
+        final ClassLoader classLoader = mLoadedApk.getSplitClassLoader(splitName);
 
-        final ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, splitName,
+        final ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, splitName,
                 mActivityToken, mUser, mFlags, classLoader);
 
         final int displayId = mDisplay != null
                 ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
 
-        context.setResources(ResourcesManager.getInstance().getResources(
-                mActivityToken,
-                mPackageInfo.getResDir(),
-                paths,
-                mPackageInfo.getOverlayDirs(),
-                mPackageInfo.getApplicationInfo().sharedLibraryFiles,
-                displayId,
-                null,
-                mPackageInfo.getCompatibilityInfo(),
-                classLoader));
+        context.setResources(mLoadedApk.getOrCreateResourcesForSplit(splitName,
+                mActivityToken, displayId));
         return context;
     }
 
@@ -2102,11 +2074,11 @@
             throw new IllegalArgumentException("overrideConfiguration must not be null");
         }
 
-        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
+        ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, mSplitName,
                 mActivityToken, mUser, mFlags, mClassLoader);
 
         final int displayId = mDisplay != null ? mDisplay.getDisplayId() : Display.DEFAULT_DISPLAY;
-        context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
+        context.setResources(mLoadedApk.createResources(mActivityToken, mSplitName, displayId,
                 overrideConfiguration, getDisplayAdjustments(displayId).getCompatibilityInfo()));
         return context;
     }
@@ -2117,11 +2089,11 @@
             throw new IllegalArgumentException("display must not be null");
         }
 
-        ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mSplitName,
+        ContextImpl context = new ContextImpl(this, mMainThread, mLoadedApk, mSplitName,
                 mActivityToken, mUser, mFlags, mClassLoader);
 
         final int displayId = display.getDisplayId();
-        context.setResources(createResources(mActivityToken, mPackageInfo, mSplitName, displayId,
+        context.setResources(mLoadedApk.createResources(mActivityToken, mSplitName, displayId,
                 null, getDisplayAdjustments(displayId).getCompatibilityInfo()));
         context.mDisplay = display;
         return context;
@@ -2131,7 +2103,7 @@
     public Context createDeviceProtectedStorageContext() {
         final int flags = (mFlags & ~Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE)
                 | Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
-        return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
+        return new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser,
                 flags, mClassLoader);
     }
 
@@ -2139,7 +2111,7 @@
     public Context createCredentialProtectedStorageContext() {
         final int flags = (mFlags & ~Context.CONTEXT_DEVICE_PROTECTED_STORAGE)
                 | Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
-        return new ContextImpl(this, mMainThread, mPackageInfo, mSplitName, mActivityToken, mUser,
+        return new ContextImpl(this, mMainThread, mLoadedApk, mSplitName, mActivityToken, mUser,
                 flags, mClassLoader);
     }
 
@@ -2188,14 +2160,14 @@
 
     @Override
     public File getDataDir() {
-        if (mPackageInfo != null) {
+        if (mLoadedApk != null) {
             File res = null;
             if (isCredentialProtectedStorage()) {
-                res = mPackageInfo.getCredentialProtectedDataDirFile();
+                res = mLoadedApk.getCredentialProtectedDataDirFile();
             } else if (isDeviceProtectedStorage()) {
-                res = mPackageInfo.getDeviceProtectedDataDirFile();
+                res = mLoadedApk.getDeviceProtectedDataDirFile();
             } else {
-                res = mPackageInfo.getDataDirFile();
+                res = mLoadedApk.getDataDirFile();
             }
 
             if (res != null) {
@@ -2246,10 +2218,10 @@
     }
 
     static ContextImpl createSystemContext(ActivityThread mainThread) {
-        LoadedApk packageInfo = new LoadedApk(mainThread);
-        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
+        LoadedApk loadedApk = new LoadedApk(mainThread);
+        ContextImpl context = new ContextImpl(null, mainThread, loadedApk, null, null, null, 0,
                 null);
-        context.setResources(packageInfo.getResources());
+        context.setResources(loadedApk.getResources());
         context.mResources.updateConfiguration(context.mResourcesManager.getConfiguration(),
                 context.mResourcesManager.getDisplayMetrics());
         return context;
@@ -2260,35 +2232,35 @@
      * Make sure that the created system UI context shares the same LoadedApk as the system context.
      */
     static ContextImpl createSystemUiContext(ContextImpl systemContext) {
-        final LoadedApk packageInfo = systemContext.mPackageInfo;
-        ContextImpl context = new ContextImpl(null, systemContext.mMainThread, packageInfo, null,
+        final LoadedApk loadedApk = systemContext.mLoadedApk;
+        ContextImpl context = new ContextImpl(null, systemContext.mMainThread, loadedApk, null,
                 null, null, 0, null);
-        context.setResources(createResources(null, packageInfo, null, Display.DEFAULT_DISPLAY, null,
-                packageInfo.getCompatibilityInfo()));
+        context.setResources(loadedApk.createResources(null, null, Display.DEFAULT_DISPLAY, null,
+                loadedApk.getCompatibilityInfo()));
         return context;
     }
 
-    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk packageInfo) {
-        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
-        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, null, null, null, 0,
+    static ContextImpl createAppContext(ActivityThread mainThread, LoadedApk loadedApk) {
+        if (loadedApk == null) throw new IllegalArgumentException("loadedApk");
+        ContextImpl context = new ContextImpl(null, mainThread, loadedApk, null, null, null, 0,
                 null);
-        context.setResources(packageInfo.getResources());
+        context.setResources(loadedApk.getResources());
         return context;
     }
 
     static ContextImpl createActivityContext(ActivityThread mainThread,
-            LoadedApk packageInfo, ActivityInfo activityInfo, IBinder activityToken, int displayId,
+            LoadedApk loadedApk, ActivityInfo activityInfo, IBinder activityToken, int displayId,
             Configuration overrideConfiguration) {
-        if (packageInfo == null) throw new IllegalArgumentException("packageInfo");
+        if (loadedApk == null) throw new IllegalArgumentException("loadedApk");
 
-        String[] splitDirs = packageInfo.getSplitResDirs();
-        ClassLoader classLoader = packageInfo.getClassLoader();
+        String[] splitDirs = loadedApk.getSplitResDirs();
+        ClassLoader classLoader = loadedApk.getClassLoader();
 
-        if (packageInfo.getApplicationInfo().requestsIsolatedSplitLoading()) {
+        if (loadedApk.getApplicationInfo().requestsIsolatedSplitLoading()) {
             Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "SplitDependencies");
             try {
-                classLoader = packageInfo.getSplitClassLoader(activityInfo.splitName);
-                splitDirs = packageInfo.getSplitPaths(activityInfo.splitName);
+                classLoader = loadedApk.getSplitClassLoader(activityInfo.splitName);
+                splitDirs = loadedApk.getSplitPaths(activityInfo.splitName);
             } catch (NameNotFoundException e) {
                 // Nothing above us can handle a NameNotFoundException, better crash.
                 throw new RuntimeException(e);
@@ -2297,14 +2269,14 @@
             }
         }
 
-        ContextImpl context = new ContextImpl(null, mainThread, packageInfo, activityInfo.splitName,
+        ContextImpl context = new ContextImpl(null, mainThread, loadedApk, activityInfo.splitName,
                 activityToken, null, 0, classLoader);
 
         // Clamp display ID to DEFAULT_DISPLAY if it is INVALID_DISPLAY.
         displayId = (displayId != Display.INVALID_DISPLAY) ? displayId : Display.DEFAULT_DISPLAY;
 
         final CompatibilityInfo compatInfo = (displayId == Display.DEFAULT_DISPLAY)
-                ? packageInfo.getCompatibilityInfo()
+                ? loadedApk.getCompatibilityInfo()
                 : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
 
         final ResourcesManager resourcesManager = ResourcesManager.getInstance();
@@ -2312,10 +2284,10 @@
         // Create the base resources for which all configuration contexts for this Activity
         // will be rebased upon.
         context.setResources(resourcesManager.createBaseActivityResources(activityToken,
-                packageInfo.getResDir(),
+                loadedApk.getResDir(),
                 splitDirs,
-                packageInfo.getOverlayDirs(),
-                packageInfo.getApplicationInfo().sharedLibraryFiles,
+                loadedApk.getOverlayDirs(),
+                loadedApk.getApplicationInfo().sharedLibraryFiles,
                 displayId,
                 overrideConfiguration,
                 compatInfo,
@@ -2326,7 +2298,7 @@
     }
 
     private ContextImpl(@Nullable ContextImpl container, @NonNull ActivityThread mainThread,
-            @NonNull LoadedApk packageInfo, @Nullable String splitName,
+            @NonNull LoadedApk loadedApk, @Nullable String splitName,
             @Nullable IBinder activityToken, @Nullable UserHandle user, int flags,
             @Nullable ClassLoader classLoader) {
         mOuterContext = this;
@@ -2335,10 +2307,10 @@
         // location for application.
         if ((flags & (Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE
                 | Context.CONTEXT_DEVICE_PROTECTED_STORAGE)) == 0) {
-            final File dataDir = packageInfo.getDataDirFile();
-            if (Objects.equals(dataDir, packageInfo.getCredentialProtectedDataDirFile())) {
+            final File dataDir = loadedApk.getDataDirFile();
+            if (Objects.equals(dataDir, loadedApk.getCredentialProtectedDataDirFile())) {
                 flags |= Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
-            } else if (Objects.equals(dataDir, packageInfo.getDeviceProtectedDataDirFile())) {
+            } else if (Objects.equals(dataDir, loadedApk.getDeviceProtectedDataDirFile())) {
                 flags |= Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
             }
         }
@@ -2352,7 +2324,7 @@
         }
         mUser = user;
 
-        mPackageInfo = packageInfo;
+        mLoadedApk = loadedApk;
         mSplitName = splitName;
         mClassLoader = classLoader;
         mResourcesManager = ResourcesManager.getInstance();
@@ -2363,8 +2335,8 @@
             setResources(container.mResources);
             mDisplay = container.mDisplay;
         } else {
-            mBasePackageName = packageInfo.mPackageName;
-            ApplicationInfo ainfo = packageInfo.getApplicationInfo();
+            mBasePackageName = loadedApk.mPackageName;
+            ApplicationInfo ainfo = loadedApk.getApplicationInfo();
             if (ainfo.uid == Process.SYSTEM_UID && ainfo.uid != Process.myUid()) {
                 // Special case: system components allow themselves to be loaded in to other
                 // processes.  For purposes of app ops, we must then consider the context as
@@ -2387,7 +2359,7 @@
     }
 
     void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
-        mPackageInfo.installSystemApplicationInfo(info, classLoader);
+        mLoadedApk.installSystemApplicationInfo(info, classLoader);
     }
 
     final void scheduleFinalCleanup(String who, String what) {
@@ -2396,7 +2368,7 @@
 
     final void performFinalCleanup(String who, String what) {
         //Log.i(TAG, "Cleanup up context: " + this);
-        mPackageInfo.removeContextRegistrations(getOuterContext(), who, what);
+        mLoadedApk.removeContextRegistrations(getOuterContext(), who, what);
     }
 
     final Context getReceiverRestrictedContext() {
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 490b2bf..41324d0 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1209,10 +1209,10 @@
     }
 
     private AppComponentFactory getFactory(String pkg) {
-        LoadedApk apk = mThread.peekPackageInfo(pkg, true);
+        LoadedApk loadedApk = mThread.peekLoadedApk(pkg, true);
         // This is in the case of starting up "android".
-        if (apk == null) apk = mThread.getSystemContext().mPackageInfo;
-        return apk.getAppFactory();
+        if (loadedApk == null) loadedApk = mThread.getSystemContext().mLoadedApk;
+        return loadedApk.getAppFactory();
     }
 
     private void prePerformCreate(Activity activity) {
diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java
index 9ec7f41..88e2356 100644
--- a/core/java/android/app/LauncherActivity.java
+++ b/core/java/android/app/LauncherActivity.java
@@ -166,7 +166,7 @@
                 if (item.icon == null) {
                     item.icon = mIconResizer.createIconThumbnail(item.resolveInfo.loadIcon(getPackageManager()));
                 }
-                text.setCompoundDrawablesWithIntrinsicBounds(item.icon, null, null, null);
+                text.setCompoundDrawablesRelativeWithIntrinsicBounds(item.icon, null, null, null);
             }
         }
 
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index ab00a7d..26f4980 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -31,6 +31,7 @@
 import android.content.pm.split.SplitDependencyLoader;
 import android.content.res.AssetManager;
 import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.os.Build;
 import android.os.Bundle;
@@ -48,15 +49,13 @@
 import android.util.AndroidRuntimeException;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.LogPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayAdjustments;
-
 import com.android.internal.util.ArrayUtils;
-
 import dalvik.system.VMRuntime;
-
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -968,14 +967,78 @@
                 throw new AssertionError("null split not found");
             }
 
-            mResources = ResourcesManager.getInstance().getResources(null, mResDir,
-                    splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
-                    Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
+            mResources = ResourcesManager.getInstance().getResources(
+                    null,
+                    mResDir,
+                    splitPaths,
+                    mOverlayDirs,
+                    mApplicationInfo.sharedLibraryFiles,
+                    Display.DEFAULT_DISPLAY,
+                    null,
+                    getCompatibilityInfo(),
                     getClassLoader());
         }
         return mResources;
     }
 
+    public Resources getOrCreateResourcesForSplit(@NonNull String splitName,
+            @Nullable IBinder activityToken, int displayId) throws NameNotFoundException {
+        return ResourcesManager.getInstance().getResources(
+                activityToken,
+                mResDir,
+                getSplitPaths(splitName),
+                mOverlayDirs,
+                mApplicationInfo.sharedLibraryFiles,
+                displayId,
+                null,
+                getCompatibilityInfo(),
+                getSplitClassLoader(splitName));
+    }
+
+    /**
+     * Creates the top level resources for the given package. Will return an existing
+     * Resources if one has already been created.
+     */
+    public Resources getOrCreateTopLevelResources(@NonNull ApplicationInfo appInfo) {
+        // Request for this app, short circuit
+        if (appInfo.uid == Process.myUid()) {
+            return getResources();
+        }
+
+        // Get resources for a different package
+        return ResourcesManager.getInstance().getResources(
+                null,
+                appInfo.publicSourceDir,
+                appInfo.splitPublicSourceDirs,
+                appInfo.resourceDirs,
+                appInfo.sharedLibraryFiles,
+                Display.DEFAULT_DISPLAY,
+                null,
+                getCompatibilityInfo(),
+                getClassLoader());
+    }
+
+    public Resources createResources(IBinder activityToken, String splitName,
+            int displayId, Configuration overrideConfig, CompatibilityInfo compatInfo) {
+        final String[] splitResDirs;
+        final ClassLoader classLoader;
+        try {
+            splitResDirs = getSplitPaths(splitName);
+            classLoader = getSplitClassLoader(splitName);
+        } catch (NameNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+        return ResourcesManager.getInstance().getResources(activityToken,
+                mResDir,
+                splitResDirs,
+                mOverlayDirs,
+                mApplicationInfo.sharedLibraryFiles,
+                displayId,
+                overrideConfig,
+                compatInfo,
+                classLoader);
+    }
+
     public Application makeApplication(boolean forceDefaultAppClass,
             Instrumentation instrumentation) {
         if (mApplication != null) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 3c4036b..89df421 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -64,6 +64,9 @@
 import android.security.Credentials;
 import android.security.KeyChain;
 import android.security.KeyChainException;
+import android.security.keymaster.KeymasterCertificateChain;
+import android.security.keystore.AttestationUtils;
+import android.security.keystore.KeyAttestationException;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.ParcelableKeyGenParameterSpec;
 import android.service.restrictions.RestrictionsReceiver;
@@ -2662,6 +2665,28 @@
     }
 
     /**
+     * When called by a profile owner of a managed profile returns true if the profile uses unified
+     * challenge with its parent user.
+     *
+     * <strong>Note: This method is not concerned with password quality and will return false if
+     * the profile has empty password as a separate challenge.
+     *
+     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+     * @throws SecurityException if {@code admin} is not a profile owner of a managed profile.
+     * @see UserManager#DISALLOW_UNIFIED_PASSWORD
+     */
+    public boolean isUsingUnifiedPassword(@NonNull ComponentName admin) {
+        if (mService != null) {
+            try {
+                return mService.isUsingUnifiedPassword(admin);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        return true;
+    }
+
+    /**
      * Determine whether the current profile password the user has set is sufficient
      * to meet the policy requirements (e.g. quality, minimum length) that have been
      * requested by the admins of the parent user and its profiles.
@@ -4005,15 +4030,27 @@
         try {
             final ParcelableKeyGenParameterSpec parcelableSpec =
                     new ParcelableKeyGenParameterSpec(keySpec);
+            KeymasterCertificateChain attestationChain = new KeymasterCertificateChain();
             final boolean success = mService.generateKeyPair(
-                    admin, mContext.getPackageName(), algorithm, parcelableSpec);
+                    admin, mContext.getPackageName(), algorithm, parcelableSpec, attestationChain);
             if (!success) {
                 Log.e(TAG, "Error generating key via DevicePolicyManagerService.");
                 return null;
             }
 
-            final KeyPair keyPair = KeyChain.getKeyPair(mContext, keySpec.getKeystoreAlias());
-            return new AttestedKeyPair(keyPair, null);
+            final String alias = keySpec.getKeystoreAlias();
+            final KeyPair keyPair = KeyChain.getKeyPair(mContext, alias);
+            Certificate[] outputChain = null;
+            try {
+                if (AttestationUtils.isChainValid(attestationChain)) {
+                    outputChain = AttestationUtils.parseCertificateChain(attestationChain);
+                }
+            } catch (KeyAttestationException e) {
+                Log.e(TAG, "Error parsing attestation chain for alias " + alias, e);
+                mService.removeKeyPair(admin, mContext.getPackageName(), alias);
+                return null;
+            }
+            return new AttestedKeyPair(keyPair, outputChain);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         } catch (KeyChainException e) {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 014d7b9..9128208 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -36,6 +36,7 @@
 import android.os.PersistableBundle;
 import android.os.RemoteCallback;
 import android.os.UserHandle;
+import android.security.keymaster.KeymasterCertificateChain;
 import android.security.keystore.ParcelableKeyGenParameterSpec;
 
 import java.util.List;
@@ -79,6 +80,7 @@
 
     boolean isActivePasswordSufficient(int userHandle, boolean parent);
     boolean isProfileActivePasswordSufficientForParent(int userHandle);
+    boolean isUsingUnifiedPassword(in ComponentName admin);
     int getCurrentFailedPasswordAttempts(int userHandle, boolean parent);
     int getProfileWithMinimumFailedPasswordsForWipe(int userHandle, boolean parent);
 
@@ -166,7 +168,9 @@
             in byte[] certBuffer, in byte[] certChainBuffer, String alias, boolean requestAccess,
             boolean isUserSelectable);
     boolean removeKeyPair(in ComponentName who, in String callerPackage, String alias);
-    boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm, in ParcelableKeyGenParameterSpec keySpec);
+    boolean generateKeyPair(in ComponentName who, in String callerPackage, in String algorithm,
+            in ParcelableKeyGenParameterSpec keySpec,
+            out KeymasterCertificateChain attestationChain);
     void choosePrivateKeyAlias(int uid, in Uri uri, in String alias, IBinder aliasCallback);
 
     void setDelegatedScopes(in ComponentName who, in String delegatePackage, in List<String> scopes);
diff --git a/core/java/android/app/servertransaction/PendingTransactionActions.java b/core/java/android/app/servertransaction/PendingTransactionActions.java
index 073d28c..8304c1c 100644
--- a/core/java/android/app/servertransaction/PendingTransactionActions.java
+++ b/core/java/android/app/servertransaction/PendingTransactionActions.java
@@ -134,7 +134,7 @@
                 Bundle.dumpStats(pw, mPersistentState);
 
                 if (ex instanceof TransactionTooLargeException
-                        && mActivity.packageInfo.getTargetSdkVersion() < Build.VERSION_CODES.N) {
+                        && mActivity.loadedApk.getTargetSdkVersion() < Build.VERSION_CODES.N) {
                     Log.e(TAG, "App sent too much data in instance state, so it was ignored", ex);
                     return;
                 }
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index b13067e..1aff7e9 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -125,6 +125,11 @@
      */
     public static final String HINT_HIDDEN = "hidden";
     /**
+     * Hint indicating this content should be shown instead of the normal content when the slice
+     * is in small format.
+     */
+    public static final String HINT_SUMMARY = "summary";
+    /**
      * Hint to indicate that this content has a toggle action associated with it. To indicate that
      * the toggle is on, use {@link #HINT_SELECTED}. When the toggle state changes, the intent
      * associated with it will be sent along with an extra {@link #EXTRA_TOGGLE_STATE} which can be
@@ -151,7 +156,6 @@
 
     /**
      * Key to retrieve an extra added to an intent when a control is changed.
-     * @hide
      */
     public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
     /**
@@ -171,6 +175,13 @@
      * Subtype to tag an item represents a slider.
      */
     public static final String SUBTYPE_SLIDER = "slider";
+    /**
+     * Subtype to indicate that this content has a toggle action associated with it. To indicate
+     * that the toggle is on, use {@link #HINT_SELECTED}. When the toggle state changes, the
+     * intent associated with it will be sent along with an extra {@link #EXTRA_TOGGLE_STATE}
+     * which can be retrieved to see the new state of the toggle.
+     */
+    public static final String SUBTYPE_TOGGLE = "toggle";
 
     private final SliceItem[] mItems;
     private final @SliceHint String[] mHints;
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index f8e19c1..0c5f225d 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -16,24 +16,37 @@
 
 package android.app.slice;
 
+import android.annotation.NonNull;
 import android.annotation.SystemService;
-import android.app.slice.ISliceListener.Stub;
 import android.content.Context;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ServiceManager.ServiceNotFoundException;
+import android.util.ArrayMap;
+import android.util.Pair;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Executor;
 
 /**
- * @hide
+ * Class to handle interactions with {@link Slice}s.
+ * <p>
+ * The SliceManager manages permissions and pinned state for slices.
  */
 @SystemService(Context.SLICE_SERVICE)
 public class SliceManager {
 
     private final ISliceManager mService;
     private final Context mContext;
+    private final ArrayMap<Pair<Uri, SliceCallback>, ISliceListener> mListenerLookup =
+            new ArrayMap<>();
 
+    /**
+     * @hide
+     */
     public SliceManager(Context context, Handler handler) throws ServiceNotFoundException {
         mContext = context;
         mService = ISliceManager.Stub.asInterface(
@@ -41,38 +54,142 @@
     }
 
     /**
+     * Adds a callback to a specific slice uri.
+     * <p>
+     * This is a convenience that performs a few slice actions at once. It will put
+     * the slice in a pinned state since there is a callback attached. It will also
+     * listen for content changes, when a content change observes, the android system
+     * will bind the new slice and provide it to all registered {@link SliceCallback}s.
+     *
+     * @param uri The uri of the slice being listened to.
+     * @param callback The listener that should receive the callbacks.
+     * @param specs The list of supported {@link SliceSpec}s of the callback.
+     * @see SliceProvider#onSlicePinned(Uri)
      */
-    public void addSliceListener(Uri uri, SliceListener listener, SliceSpec[] specs) {
+    public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback,
+            @NonNull List<SliceSpec> specs) {
+        registerSliceCallback(uri, callback, specs, Handler.getMain());
+    }
+
+    /**
+     * Adds a callback to a specific slice uri.
+     * <p>
+     * This is a convenience that performs a few slice actions at once. It will put
+     * the slice in a pinned state since there is a callback attached. It will also
+     * listen for content changes, when a content change observes, the android system
+     * will bind the new slice and provide it to all registered {@link SliceCallback}s.
+     *
+     * @param uri The uri of the slice being listened to.
+     * @param callback The listener that should receive the callbacks.
+     * @param specs The list of supported {@link SliceSpec}s of the callback.
+     * @see SliceProvider#onSlicePinned(Uri)
+     */
+    public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback,
+            @NonNull List<SliceSpec> specs, Handler handler) {
         try {
-            mService.addSliceListener(uri, mContext.getPackageName(), listener.mStub, specs);
+            mService.addSliceListener(uri, mContext.getPackageName(),
+                    getListener(uri, callback, new ISliceListener.Stub() {
+                        @Override
+                        public void onSliceUpdated(Slice s) throws RemoteException {
+                            handler.post(() -> callback.onSliceUpdated(s));
+                        }
+                    }), specs.toArray(new SliceSpec[specs.size()]));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
+     * Adds a callback to a specific slice uri.
+     * <p>
+     * This is a convenience that performs a few slice actions at once. It will put
+     * the slice in a pinned state since there is a callback attached. It will also
+     * listen for content changes, when a content change observes, the android system
+     * will bind the new slice and provide it to all registered {@link SliceCallback}s.
+     *
+     * @param uri The uri of the slice being listened to.
+     * @param callback The listener that should receive the callbacks.
+     * @param specs The list of supported {@link SliceSpec}s of the callback.
+     * @see SliceProvider#onSlicePinned(Uri)
      */
-    public void removeSliceListener(Uri uri, SliceListener listener) {
+    public void registerSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback,
+            @NonNull List<SliceSpec> specs, Executor executor) {
         try {
-            mService.removeSliceListener(uri, mContext.getPackageName(), listener.mStub);
+            mService.addSliceListener(uri, mContext.getPackageName(),
+                    getListener(uri, callback, new ISliceListener.Stub() {
+                        @Override
+                        public void onSliceUpdated(Slice s) throws RemoteException {
+                            executor.execute(() -> callback.onSliceUpdated(s));
+                        }
+                    }), specs.toArray(new SliceSpec[specs.size()]));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private ISliceListener getListener(Uri uri, SliceCallback callback,
+            ISliceListener listener) {
+        Pair<Uri, SliceCallback> key = new Pair<>(uri, callback);
+        if (mListenerLookup.containsKey(key)) {
+            try {
+                mService.removeSliceListener(uri, mContext.getPackageName(),
+                        mListenerLookup.get(key));
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+        mListenerLookup.put(key, listener);
+        return listener;
+    }
+
+    /**
+     * Removes a callback for a specific slice uri.
+     * <p>
+     * Removes the app from the pinned state (if there are no other apps/callbacks pinning it)
+     * in addition to removing the callback.
+     *
+     * @param uri The uri of the slice being listened to
+     * @param callback The listener that should no longer receive callbacks.
+     * @see #registerSliceCallback
+     */
+    public void unregisterSliceCallback(@NonNull Uri uri, @NonNull SliceCallback callback) {
+        try {
+            mService.removeSliceListener(uri, mContext.getPackageName(),
+                    mListenerLookup.remove(new Pair<>(uri, callback)));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
+     * Ensures that a slice is in a pinned state.
+     * <p>
+     * Pinned state is not persisted across reboots, so apps are expected to re-pin any slices
+     * they still care about after a reboot.
+     *
+     * @param uri The uri of the slice being pinned.
+     * @param specs The list of supported {@link SliceSpec}s of the callback.
+     * @see SliceProvider#onSlicePinned(Uri)
      */
-    public void pinSlice(Uri uri, SliceSpec[] specs) {
+    public void pinSlice(@NonNull Uri uri, @NonNull List<SliceSpec> specs) {
         try {
-            mService.pinSlice(mContext.getPackageName(), uri, specs);
+            mService.pinSlice(mContext.getPackageName(), uri,
+                    specs.toArray(new SliceSpec[specs.size()]));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
+     * Remove a pin for a slice.
+     * <p>
+     * If the slice has no other pins/callbacks then the slice will be unpinned.
+     *
+     * @param uri The uri of the slice being unpinned.
+     * @see #pinSlice
+     * @see SliceProvider#onSliceUnpinned(Uri)
      */
-    public void unpinSlice(Uri uri) {
+    public void unpinSlice(@NonNull Uri uri) {
         try {
             mService.unpinSlice(mContext.getPackageName(), uri);
         } catch (RemoteException e) {
@@ -81,6 +198,7 @@
     }
 
     /**
+     * @hide
      */
     public boolean hasSliceAccess() {
         try {
@@ -91,41 +209,31 @@
     }
 
     /**
+     * Get the current set of specs for a pinned slice.
+     * <p>
+     * This is the set of specs supported for a specific pinned slice. It will take
+     * into account all clients and returns only specs supported by all.
+     * @see SliceSpec
      */
-    public SliceSpec[] getPinnedSpecs(Uri uri) {
+    public @NonNull List<SliceSpec> getPinnedSpecs(Uri uri) {
         try {
-            return mService.getPinnedSpecs(uri, mContext.getPackageName());
+            return Arrays.asList(mService.getPinnedSpecs(uri, mContext.getPackageName()));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
+     * Class that listens to changes in {@link Slice}s.
      */
-    public abstract static class SliceListener {
-        private final Handler mHandler;
+    public interface SliceCallback {
 
         /**
+         * Called when slice is updated.
+         *
+         * @param s The updated slice.
+         * @see #registerSliceCallback
          */
-        public SliceListener() {
-            this(Handler.getMain());
-        }
-
-        /**
-         */
-        public SliceListener(Handler h) {
-            mHandler = h;
-        }
-
-        /**
-         */
-        public abstract void onSliceUpdated(Slice s);
-
-        private final ISliceListener.Stub mStub = new Stub() {
-            @Override
-            public void onSliceUpdated(Slice s) throws RemoteException {
-                mHandler.post(() -> SliceListener.this.onSliceUpdated(s));
-            }
-        };
+        void onSliceUpdated(Slice s);
     }
 }
diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java
index 7dcd2fe..8483931 100644
--- a/core/java/android/app/slice/SliceProvider.java
+++ b/core/java/android/app/slice/SliceProvider.java
@@ -151,13 +151,33 @@
     }
 
     /**
-     * @hide
+     * Called to inform an app that a slice has been pinned.
+     * <p>
+     * Pinning is a way that slice hosts use to notify apps of which slices
+     * they care about updates for. When a slice is pinned the content is
+     * expected to be relatively fresh and kept up to date.
+     * <p>
+     * Being pinned does not provide any escalated privileges for the slice
+     * provider. So apps should do things such as turn on syncing or schedule
+     * a job in response to a onSlicePinned.
+     * <p>
+     * Pinned state is not persisted through a reboot, and apps can expect a
+     * new call to onSlicePinned for any slices that should remain pinned
+     * after a reboot occurs.
+     *
+     * @param sliceUri The uri of the slice being unpinned.
+     * @see #onSliceUnpinned(Uri)
      */
     public void onSlicePinned(Uri sliceUri) {
     }
 
     /**
-     * @hide
+     * Called to inform an app that a slices is no longer pinned.
+     * <p>
+     * This means that no other apps on the device care about updates to this
+     * slice anymore and therefore it is not important to be updated. Any syncs
+     * or jobs related to this slice should be cancelled.
+     * @see #onSlicePinned(Uri)
      */
     public void onSliceUnpinned(Uri sliceUri) {
     }
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index 4a00486..f38e462 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -35,8 +35,6 @@
  *
  * <p>BluetoothHidDevice is a proxy object for controlling the Bluetooth HID Device Service via IPC.
  * Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHidDevice proxy object.
- *
- * <p>{@hide}
  */
 public final class BluetoothHidDevice implements BluetoothProfile {
 
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
index 4609d52..c05df2d 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java
@@ -26,8 +26,6 @@
  * registration.
  *
  * <p>{@see BluetoothHidDevice}
- *
- * <p>{@hide}
  */
 public final class BluetoothHidDeviceAppQosSettings implements Parcelable {
 
@@ -46,10 +44,8 @@
 
     /**
      * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. The QoS
-     * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder. {@see <a
-     * href="https://www.bluetooth.com/specifications/profiles-overview">
-     * https://www.bluetooth.com/specifications/profiles-overview </a> Bluetooth HID Specfication
-     * v1.1.1 Section 5.2 and Appendix D }
+     * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder.
+     * Please refer to Bluetooth HID Specfication v1.1.1 Section 5.2 and Appendix D for parameters.
      *
      * @param serviceType L2CAP service type
      * @param tokenRate L2CAP token rate
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
index 2da64e5..562c559 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java
@@ -28,8 +28,6 @@
  * Android device can be discovered as a Bluetooth HID Device.
  *
  * <p>{@see BluetoothHidDevice}
- *
- * <p>{@hide}
  */
 public final class BluetoothHidDeviceAppSdpSettings implements Parcelable {
 
diff --git a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
index 6ed1965..bd19955 100644
--- a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
+++ b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java
@@ -24,8 +24,6 @@
  * registration.
  *
  * <p>{@see BluetoothHidDevice}
- *
- * <p>{@hide}
  */
 public abstract class BluetoothHidDeviceCallback {
 
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index ebbc710..df2028a 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -153,8 +153,6 @@
 
     /**
      * HID Device
-     *
-     * @hide
      */
     public static final int HID_DEVICE = 19;
 
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 2bbcfff..e2fd82d 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1559,7 +1559,7 @@
             throws PackageParserException {
         final String apkPath = apkFile.getAbsolutePath();
 
-        boolean untrusted = (parseFlags & PARSE_IS_SYSTEM_DIR) == 0;
+        boolean systemDir = (parseFlags & PARSE_IS_SYSTEM_DIR) != 0;
         int minSignatureScheme = ApkSignatureVerifier.VERSION_JAR_SIGNATURE_SCHEME;
         if ((parseFlags & PARSE_IS_EPHEMERAL) != 0 || pkg.applicationInfo.isStaticSharedLibrary()) {
             // must use v2 signing scheme
@@ -1567,7 +1567,7 @@
         }
         try {
             ApkSignatureVerifier.Result verified =
-                    ApkSignatureVerifier.verify(apkPath, minSignatureScheme, untrusted);
+                    ApkSignatureVerifier.verify(apkPath, minSignatureScheme, systemDir);
             if (pkg.mCertificates == null) {
                 pkg.mCertificates = verified.certs;
                 pkg.mSignatures = verified.sigs;
diff --git a/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java b/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java
new file mode 100644
index 0000000..e02e68d
--- /dev/null
+++ b/core/java/android/database/sqlite/SQLiteCompatibilityWalFlags.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database.sqlite;
+
+import android.app.ActivityThread;
+import android.app.Application;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.KeyValueListParser;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Helper class for accessing
+ * {@link Settings.Global#SQLITE_COMPATIBILITY_WAL_FLAGS global compatibility WAL settings}.
+ *
+ * <p>The value of {@link Settings.Global#SQLITE_COMPATIBILITY_WAL_FLAGS} is cached on first access
+ * for consistent behavior across all connections opened in the process.
+ * @hide
+ */
+public class SQLiteCompatibilityWalFlags {
+
+    private static final String TAG = "SQLiteCompatibilityWalFlags";
+
+    private static volatile boolean sInitialized = true; // Temporarily disable flags
+    private static volatile boolean sFlagsSet;
+    private static volatile boolean sCompatibilityWalSupported;
+    private static volatile String sWALSyncMode;
+    // This flag is used to avoid recursive initialization due to circular dependency on Settings
+    private static volatile boolean sCallingGlobalSettings;
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public static boolean areFlagsSet() {
+        initIfNeeded();
+        return sFlagsSet;
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public static boolean isCompatibilityWalSupported() {
+        initIfNeeded();
+        return sCompatibilityWalSupported;
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public static String getWALSyncMode() {
+        initIfNeeded();
+        return sWALSyncMode;
+    }
+
+    private static void initIfNeeded() {
+        if (sInitialized || sCallingGlobalSettings) {
+            return;
+        }
+        ActivityThread activityThread = ActivityThread.currentActivityThread();
+        Application app = activityThread == null ? null : activityThread.getApplication();
+        String flags = null;
+        if (app == null) {
+            Log.w(TAG, "Cannot read global setting "
+                    + Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS + " - "
+                    + "Application state not available");
+        } else {
+            try {
+                sCallingGlobalSettings = true;
+                flags = Settings.Global.getString(app.getContentResolver(),
+                        Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS);
+            } finally {
+                sCallingGlobalSettings = false;
+            }
+        }
+
+        init(flags);
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public static void init(String flags) {
+        if (TextUtils.isEmpty(flags)) {
+            sInitialized = true;
+            return;
+        }
+        KeyValueListParser parser = new KeyValueListParser(',');
+        try {
+            parser.setString(flags);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Setting has invalid format: " + flags, e);
+            sInitialized = true;
+            return;
+        }
+        sCompatibilityWalSupported = parser.getBoolean("compatibility_wal_supported",
+                SQLiteGlobal.isCompatibilityWalSupported());
+        sWALSyncMode = parser.getString("wal_syncmode", SQLiteGlobal.getWALSyncMode());
+        Log.i(TAG, "Read compatibility WAL flags: compatibility_wal_supported="
+                + sCompatibilityWalSupported + ", wal_syncmode=" + sWALSyncMode);
+        sFlagsSet = true;
+        sInitialized = true;
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public static void reset() {
+        sInitialized = false;
+        sFlagsSet = false;
+        sCompatibilityWalSupported = false;
+        sWALSyncMode = null;
+    }
+}
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 2c93a7f..7717b8d 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -296,7 +296,11 @@
                     && mConfiguration.syncMode == null && mConfiguration.useCompatibilityWal;
             if (walEnabled || useCompatibilityWal) {
                 setJournalMode("WAL");
-                setSyncMode(SQLiteGlobal.getWALSyncMode());
+                if (useCompatibilityWal && SQLiteCompatibilityWalFlags.areFlagsSet()) {
+                    setSyncMode(SQLiteCompatibilityWalFlags.getWALSyncMode());
+                } else {
+                    setSyncMode(SQLiteGlobal.getWALSyncMode());
+                }
             } else {
                 setJournalMode(mConfiguration.journalMode == null
                         ? SQLiteGlobal.getDefaultJournalMode() : mConfiguration.journalMode);
diff --git a/core/java/android/database/sqlite/SQLiteConnectionPool.java b/core/java/android/database/sqlite/SQLiteConnectionPool.java
index 5adb119..b211700 100644
--- a/core/java/android/database/sqlite/SQLiteConnectionPool.java
+++ b/core/java/android/database/sqlite/SQLiteConnectionPool.java
@@ -1094,6 +1094,12 @@
             printer.println("  Open: " + mIsOpen);
             printer.println("  Max connections: " + mMaxConnectionPoolSize);
             printer.println("  Total execution time: " + mTotalExecutionTimeCounter);
+            if (SQLiteCompatibilityWalFlags.areFlagsSet()) {
+                printer.println("  Compatibility WAL settings: compatibility_wal_supported="
+                        + SQLiteCompatibilityWalFlags
+                        .isCompatibilityWalSupported() + ", wal_syncmode="
+                        + SQLiteCompatibilityWalFlags.getWALSyncMode());
+            }
             if (mConfiguration.isLookasideConfigSet()) {
                 printer.println("  Lookaside config: sz=" + mConfiguration.lookasideSlotSize
                         + " cnt=" + mConfiguration.lookasideSlotCount);
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 09bb9c6..c1c0812 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -289,6 +289,10 @@
         mConfigurationLocked.journalMode = journalMode;
         mConfigurationLocked.syncMode = syncMode;
         mConfigurationLocked.useCompatibilityWal = SQLiteGlobal.isCompatibilityWalSupported();
+        if (!mConfigurationLocked.isInMemoryDb() && SQLiteCompatibilityWalFlags.areFlagsSet()) {
+            mConfigurationLocked.useCompatibilityWal = SQLiteCompatibilityWalFlags
+                    .isCompatibilityWalSupported();
+        }
     }
 
     @Override
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 1d66dc6d..5b89f54 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -271,7 +271,7 @@
         throw new UnsupportedOperationException("TODO: Implement this");
     }
 
-    /*
+    /**
      * Helper function to generate a stub for a non-query transaction callback.
      *
      * @param transaction the transaction to unblock when complete
@@ -297,7 +297,7 @@
         };
     }
 
-   /*
+   /**
     * Helper function to generate a stub for a query transaction callback.
     *
     * @param transaction the transaction to unblock when complete
@@ -392,7 +392,17 @@
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public ContextHubTransaction<Void> enableNanoApp(ContextHubInfo hubInfo, long nanoAppId) {
-        throw new UnsupportedOperationException("TODO: Implement this");
+        ContextHubTransaction<Void> transaction =
+                new ContextHubTransaction<>(ContextHubTransaction.TYPE_ENABLE_NANOAPP);
+        IContextHubTransactionCallback callback = createTransactionCallback(transaction);
+
+        try {
+            mService.enableNanoApp(hubInfo.getId(), callback, nanoAppId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        return transaction;
     }
 
     /**
@@ -407,7 +417,17 @@
      */
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     public ContextHubTransaction<Void> disableNanoApp(ContextHubInfo hubInfo, long nanoAppId) {
-        throw new UnsupportedOperationException("TODO: Implement this");
+        ContextHubTransaction<Void> transaction =
+                new ContextHubTransaction<>(ContextHubTransaction.TYPE_DISABLE_NANOAPP);
+        IContextHubTransactionCallback callback = createTransactionCallback(transaction);
+
+        try {
+            mService.disableNanoApp(hubInfo.getId(), callback, nanoAppId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        return transaction;
     }
 
     /**
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index 628ebc7..db5bd36 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -70,6 +70,16 @@
             int contextHubId, in IContextHubTransactionCallback transactionCallback,
             long nanoAppId);
 
+    // Enables a nanoapp at the specified hub
+    void enableNanoApp(
+            int contextHubId, in IContextHubTransactionCallback transactionCallback,
+            long nanoAppId);
+
+    // Disables a nanoapp at the specified hub
+    void disableNanoApp(
+            int contextHubId, in IContextHubTransactionCallback transactionCallback,
+            long nanoAppId);
+
     // Queries for a list of nanoapps
     void queryNanoApps(int contextHubId, in IContextHubTransactionCallback transactionCallback);
 }
diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java
index 4f4361f..4d54e31 100644
--- a/core/java/android/hardware/radio/RadioManager.java
+++ b/core/java/android/hardware/radio/RadioManager.java
@@ -161,7 +161,8 @@
         private final Set<Integer> mSupportedIdentifierTypes;
         @NonNull private final Map<String, String> mVendorInfo;
 
-        ModuleProperties(int id, String serviceName, int classId, String implementor,
+        /** @hide */
+        public ModuleProperties(int id, String serviceName, int classId, String implementor,
                 String product, String version, String serial, int numTuners, int numAudioSources,
                 boolean isCaptureSupported, BandDescriptor[] bands, boolean isBgScanSupported,
                 @ProgramSelector.ProgramType int[] supportedProgramTypes,
diff --git a/core/java/android/net/IIpSecService.aidl b/core/java/android/net/IIpSecService.aidl
index 0b1ea98..d9b57db 100644
--- a/core/java/android/net/IIpSecService.aidl
+++ b/core/java/android/net/IIpSecService.aidl
@@ -30,7 +30,7 @@
  */
 interface IIpSecService
 {
-    IpSecSpiResponse reserveSecurityParameterIndex(
+    IpSecSpiResponse allocateSecurityParameterIndex(
             int direction, in String remoteAddress, int requestedSpi, in IBinder binder);
 
     void releaseSecurityParameterIndex(int resourceId);
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index a9e60ec..6a4b891 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -46,7 +46,7 @@
  * to create a VPN should use {@link VpnService}.
  *
  * @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the
- * Internet Protocol</a>
+ *     Internet Protocol</a>
  */
 @SystemService(Context.IPSEC_SERVICE)
 public final class IpSecManager {
@@ -59,8 +59,7 @@
      *
      * @hide
      */
-    @TestApi
-    public static final int INVALID_SECURITY_PARAMETER_INDEX = 0;
+    @TestApi public static final int INVALID_SECURITY_PARAMETER_INDEX = 0;
 
     /** @hide */
     public interface Status {
@@ -78,7 +77,7 @@
      * <p>The combination of remote {@code InetAddress} and SPI must be unique across all apps on
      * one device. If this error is encountered, a new SPI is required before a transform may be
      * created. This error can be avoided by calling {@link
-     * IpSecManager#reserveSecurityParameterIndex}.
+     * IpSecManager#allocateSecurityParameterIndex}.
      */
     public static final class SpiUnavailableException extends AndroidException {
         private final int mSpi;
@@ -121,7 +120,7 @@
      * This class represents a reserved SPI.
      *
      * <p>Objects of this type are used to track reserved security parameter indices. They can be
-     * obtained by calling {@link IpSecManager#reserveSecurityParameterIndex} and must be released
+     * obtained by calling {@link IpSecManager#allocateSecurityParameterIndex} and must be released
      * by calling {@link #close()} when they are no longer needed.
      */
     public static final class SecurityParameterIndex implements AutoCloseable {
@@ -170,7 +169,7 @@
             mRemoteAddress = remoteAddress;
             try {
                 IpSecSpiResponse result =
-                        mService.reserveSecurityParameterIndex(
+                        mService.allocateSecurityParameterIndex(
                                 direction, remoteAddress.getHostAddress(), spi, new Binder());
 
                 if (result == null) {
@@ -228,7 +227,7 @@
      *     for this user
      * @throws SpiUnavailableException indicating that a particular SPI cannot be reserved
      */
-    public SecurityParameterIndex reserveSecurityParameterIndex(
+    public SecurityParameterIndex allocateSecurityParameterIndex(
             int direction, InetAddress remoteAddress) throws ResourceUnavailableException {
         try {
             return new SecurityParameterIndex(
@@ -255,7 +254,7 @@
      *     for this user
      * @throws SpiUnavailableException indicating that the requested SPI could not be reserved
      */
-    public SecurityParameterIndex reserveSecurityParameterIndex(
+    public SecurityParameterIndex allocateSecurityParameterIndex(
             int direction, InetAddress remoteAddress, int requestedSpi)
             throws SpiUnavailableException, ResourceUnavailableException {
         if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) {
@@ -273,16 +272,18 @@
      * unprotected traffic can resume on that socket.
      *
      * <p>For security reasons, the destination address of any traffic on the socket must match the
-     * remote {@code  InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
+     * remote {@code InetAddress} of the {@code IpSecTransform}. Attempts to send traffic to any
      * other IP address will result in an IOException. In addition, reads and writes on the socket
      * will throw IOException if the user deactivates the transform (by calling {@link
      * IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
      *
-     * <h4>Rekey Procedure</h4> <p>When applying a new tranform to a socket, the previous transform
-     * will be removed. However, inbound traffic on the old transform will continue to be decrypted
-     * until that transform is deallocated by calling {@link IpSecTransform#close()}. This overlap
-     * allows rekey procedures where both transforms are valid until both endpoints are using the
-     * new transform and all in-flight packets have been received.
+     * <h4>Rekey Procedure</h4>
+     *
+     * <p>When applying a new tranform to a socket, the previous transform will be removed. However,
+     * inbound traffic on the old transform will continue to be decrypted until that transform is
+     * deallocated by calling {@link IpSecTransform#close()}. This overlap allows rekey procedures
+     * where both transforms are valid until both endpoints are using the new transform and all
+     * in-flight packets have been received.
      *
      * @param socket a stream socket
      * @param transform a transport mode {@code IpSecTransform}
@@ -310,11 +311,13 @@
      * will throw IOException if the user deactivates the transform (by calling {@link
      * IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
      *
-     * <h4>Rekey Procedure</h4> <p>When applying a new tranform to a socket, the previous transform
-     * will be removed. However, inbound traffic on the old transform will continue to be decrypted
-     * until that transform is deallocated by calling {@link IpSecTransform#close()}. This overlap
-     * allows rekey procedures where both transforms are valid until both endpoints are using the
-     * new transform and all in-flight packets have been received.
+     * <h4>Rekey Procedure</h4>
+     *
+     * <p>When applying a new tranform to a socket, the previous transform will be removed. However,
+     * inbound traffic on the old transform will continue to be decrypted until that transform is
+     * deallocated by calling {@link IpSecTransform#close()}. This overlap allows rekey procedures
+     * where both transforms are valid until both endpoints are using the new transform and all
+     * in-flight packets have been received.
      *
      * @param socket a datagram socket
      * @param transform a transport mode {@code IpSecTransform}
@@ -342,11 +345,13 @@
      * will throw IOException if the user deactivates the transform (by calling {@link
      * IpSecTransform#close()}) without calling {@link #removeTransportModeTransform}.
      *
-     * <h4>Rekey Procedure</h4> <p>When applying a new tranform to a socket, the previous transform
-     * will be removed. However, inbound traffic on the old transform will continue to be decrypted
-     * until that transform is deallocated by calling {@link IpSecTransform#close()}. This overlap
-     * allows rekey procedures where both transforms are valid until both endpoints are using the
-     * new transform and all in-flight packets have been received.
+     * <h4>Rekey Procedure</h4>
+     *
+     * <p>When applying a new tranform to a socket, the previous transform will be removed. However,
+     * inbound traffic on the old transform will continue to be decrypted until that transform is
+     * deallocated by calling {@link IpSecTransform#close()}. This overlap allows rekey procedures
+     * where both transforms are valid until both endpoints are using the new transform and all
+     * in-flight packets have been received.
      *
      * @param socket a socket file descriptor
      * @param transform a transport mode {@code IpSecTransform}
@@ -379,7 +384,8 @@
      * Applications should probably not use this API directly. Instead, they should use {@link
      * VpnService} to provide VPN capability in a more generic fashion.
      *
-     * TODO: Update javadoc for tunnel mode APIs at the same time the APIs are re-worked.
+     * <p>TODO: Update javadoc for tunnel mode APIs at the same time the APIs are re-worked.
+     *
      * @param net a {@link Network} that will be tunneled via IP Sec.
      * @param transform an {@link IpSecTransform}, which must be an active Tunnel Mode transform.
      * @hide
@@ -469,7 +475,8 @@
      * all traffic that cannot be routed to the Tunnel's outbound interface. If that interface is
      * lost, all traffic will drop.
      *
-     * TODO: Update javadoc for tunnel mode APIs at the same time the APIs are re-worked.
+     * <p>TODO: Update javadoc for tunnel mode APIs at the same time the APIs are re-worked.
+     *
      * @param net a network that currently has transform applied to it.
      * @param transform a Tunnel Mode IPsec Transform that has been previously applied to the given
      *     network
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index cda4ec7..7cd742b 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -47,7 +47,7 @@
  * system resources.
  *
  * @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the
- * Internet Protocol</a>
+ *     Internet Protocol</a>
  */
 public final class IpSecTransform implements AutoCloseable {
     private static final String TAG = "IpSecTransform";
@@ -116,8 +116,7 @@
     }
 
     /**
-     * Checks the result status and throws an appropriate exception if
-     * the status is not Status.OK.
+     * Checks the result status and throws an appropriate exception if the status is not Status.OK.
      */
     private void checkResultStatus(int status)
             throws IOException, IpSecManager.ResourceUnavailableException,
@@ -267,9 +266,7 @@
         return;
     }
 
-    /**
-     * This class is used to build {@link IpSecTransform} objects.
-     */
+    /** This class is used to build {@link IpSecTransform} objects. */
     public static class Builder {
         private Context mContext;
         private IpSecConfig mConfig;
@@ -339,7 +336,7 @@
          *
          * <p>Because IPsec operates at the IP layer, this 32-bit identifier uniquely identifies
          * packets to a given destination address. To prevent SPI collisions, values should be
-         * reserved by calling {@link IpSecManager#reserveSecurityParameterIndex}.
+         * reserved by calling {@link IpSecManager#allocateSecurityParameterIndex}.
          *
          * <p>If the SPI and algorithms are omitted for one direction, traffic in that direction
          * will not be encrypted or authenticated.
@@ -374,10 +371,9 @@
          * <p>This allows IPsec traffic to pass through a NAT.
          *
          * @see <a href="https://tools.ietf.org/html/rfc3948">RFC 3948, UDP Encapsulation of IPsec
-         * ESP Packets</a>
+         *     ESP Packets</a>
          * @see <a href="https://tools.ietf.org/html/rfc7296#section-2.23">RFC 7296 section 2.23,
-         * NAT Traversal of IKEv2</a>
-         *
+         *     NAT Traversal of IKEv2</a>
          * @param localSocket a socket for sending and receiving encapsulated traffic
          * @param remotePort the UDP port number of the remote host that will send and receive
          *     encapsulated traffic. In the case of IKEv2, this should be port 4500.
@@ -402,7 +398,6 @@
          *
          * @param intervalSeconds the maximum number of seconds between keepalive packets. Must be
          *     between 20s and 3600s.
-         *
          * @hide
          */
         @SystemApi
@@ -418,7 +413,6 @@
          * will not affect any network traffic until it has been applied to one or more sockets.
          *
          * @see IpSecManager#applyTransportModeTransform
-         *
          * @param remoteAddress the remote {@code InetAddress} of traffic on sockets that will use
          *     this transform
          * @throws IllegalArgumentException indicating that a particular combination of transform
@@ -453,8 +447,8 @@
          */
         public IpSecTransform buildTunnelModeTransform(
                 InetAddress localAddress, InetAddress remoteAddress) {
-            //FIXME: argument validation here
-            //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
+            // FIXME: argument validation here
+            // throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
             mConfig.setLocalAddress(localAddress.getHostAddress());
             mConfig.setRemoteAddress(remoteAddress.getHostAddress());
             mConfig.setMode(MODE_TUNNEL);
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 3ec744d..3e8161f 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -52,7 +52,7 @@
         // pushed ones to be consistent.
         write4Bytes(STATS_BUFFER_TAG_ID);
         mStorage.write(EVENT_TYPE_LIST); // This is required to start the log entry.
-        mStorage.write(fields); // Indicate number of elements in this list.
+        mStorage.write(fields + 1); // Indicate number of elements in this list. +1 for the tag
         mStorage.write(EVENT_TYPE_INT);
         // The first element is the real atom tag number
         write4Bytes(tag);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index fb60bbb..3504142 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -774,6 +774,25 @@
     public static final String DISALLOW_OEM_UNLOCK = "no_oem_unlock";
 
     /**
+     * Specifies that the managed profile is not allowed to have unified lock screen challenge with
+     * the primary user.
+     *
+     * <p><strong>Note:</strong> Setting this restriction alone doesn't automatically set a
+     * separate challenge. Profile owner can ask the user to set a new password using
+     * {@link DevicePolicyManager#ACTION_SET_NEW_PASSWORD} and verify it using
+     * {@link DevicePolicyManager#isUsingUnifiedPassword(ComponentName)}.
+     *
+     * <p>Can be set by profile owners. It only has effect on managed profiles when set by managed
+     * profile owner. Has no effect on non-managed profiles or users.
+     * <p>Key for user restrictions.
+     * <p>Type: Boolean
+     * @see DevicePolicyManager#addUserRestriction(ComponentName, String)
+     * @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_UNIFIED_PASSWORD = "no_unified_password";
+
+    /**
      * Allows apps in the parent profile to handle web links from the managed profile.
      *
      * This user restriction has an effect only in a managed profile.
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index ec5b1c6..c94da9a 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -4244,19 +4244,39 @@
         /**
          * The flattened {@link android.content.ComponentName} of a  {@link
          * android.telecom.PhoneAccountHandle} that is the preferred {@code PhoneAccountHandle} to
-         * call the contact with. Used by {@link CommonDataKinds.Phone}.
+         * call the contact with.
          *
+         * <p> On a multi-SIM device this field can be used in a {@link CommonDataKinds.Phone} row
+         * to indicate the {@link PhoneAccountHandle} to call the number with, instead of using
+         * {@link android.telecom.TelecomManager#getDefaultOutgoingPhoneAccount(String)} or asking
+         * every time.
+         *
+         * <p>{@link android.telecom.TelecomManager#placeCall(Uri, android.os.Bundle)}
+         * should be called with {@link android.telecom.TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE}
+         * set to the {@link PhoneAccountHandle} using the {@link ComponentName} from this field.
+         *
+         * @see #PREFERRED_PHONE_ACCOUNT_ID
          * @see PhoneAccountHandle#getComponentName()
          * @see ComponentName#flattenToString()
          */
         String PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME = "preferred_phone_account_component_name";
 
         /**
-         * The ID of a  {@link
+         * The ID of a {@link
          * android.telecom.PhoneAccountHandle} that is the preferred {@code PhoneAccountHandle} to
          * call the contact with. Used by {@link CommonDataKinds.Phone}.
          *
-         * @see PhoneAccountHandle#getId() ()
+         * <p> On a multi-SIM device this field can be used in a {@link CommonDataKinds.Phone} row
+         * to indicate the {@link PhoneAccountHandle} to call the number with, instead of using
+         * {@link android.telecom.TelecomManager#getDefaultOutgoingPhoneAccount(String)} or asking
+         * every time.
+         *
+         * <p>{@link android.telecom.TelecomManager#placeCall(Uri, android.os.Bundle)}
+         * should be called with {@link android.telecom.TelecomManager#EXTRA_PHONE_ACCOUNT_HANDLE}
+         * set to the {@link PhoneAccountHandle} using the id from this field.
+         *
+         * @see #PREFERRED_PHONE_ACCOUNT_COMPONENT_NAME
+         * @see PhoneAccountHandle#getId()
          */
         String PREFERRED_PHONE_ACCOUNT_ID = "preferred_phone_account_id";
     }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4ecbdf5..d0c1003 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5328,48 +5328,51 @@
         public static final String AUTOFILL_SERVICE = "autofill_service";
 
         /**
-         * Experimental autofill feature.
+         * Boolean indicating if Autofill supports field classification.
          *
-         * <p>TODO(b/67867469): document (or remove) once feature is finished
+         * @see android.service.autofill.AutofillService
+         *
          * @hide
          */
+        @SystemApi
         @TestApi
         public static final String AUTOFILL_FEATURE_FIELD_CLASSIFICATION =
                 "autofill_field_classification";
 
         /**
-         * Experimental autofill feature.
+         * Defines value returned by {@link android.service.autofill.UserData#getMaxUserDataSize()}.
          *
-         * <p>TODO(b/67867469): document (or remove) once feature is finished
          * @hide
          */
+        @SystemApi
         public static final String AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE =
                 "autofill_user_data_max_user_data_size";
 
         /**
-         * Experimental autofill feature.
+         * Defines value returned by
+         * {@link android.service.autofill.UserData#getMaxFieldClassificationIdsSize()}.
          *
-         * <p>TODO(b/67867469): document (or remove) once feature is finished
          * @hide
          */
+        @SystemApi
         public static final String AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE =
                 "autofill_user_data_max_field_classification_size";
 
         /**
-         * Experimental autofill feature.
+         * Defines value returned by {@link android.service.autofill.UserData#getMaxValueLength()}.
          *
-         * <p>TODO(b/67867469): document (or remove) once feature is finished
          * @hide
          */
+        @SystemApi
         public static final String AUTOFILL_USER_DATA_MAX_VALUE_LENGTH =
                 "autofill_user_data_max_value_length";
 
         /**
-         * Experimental autofill feature.
+         * Defines value returned by {@link android.service.autofill.UserData#getMinValueLength()}.
          *
-         * <p>TODO(b/67867469): document (or remove) once feature is finished
          * @hide
          */
+        @SystemApi
         public static final String AUTOFILL_USER_DATA_MIN_VALUE_LENGTH =
                 "autofill_user_data_min_value_length";
 
@@ -11145,6 +11148,19 @@
                 "notification_snooze_options";
 
         /**
+         * Configuration flags for SQLite Compatibility WAL. Encoded as a key-value list, separated
+         * by commas. E.g.: compatibility_wal_supported=true, wal_syncmode=OFF
+         *
+         * Supported keys:
+         * compatibility_wal_supported      (boolean)
+         * wal_syncmode       (String)
+         *
+         * @hide
+         */
+        public static final String SQLITE_COMPATIBILITY_WAL_FLAGS =
+                "sqlite_compatibility_wal_flags";
+
+        /**
          * Enable GNSS Raw Measurements Full Tracking?
          * 0 = no
          * 1 = yes
diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index 6be0e76..6a3c55e 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -297,7 +297,7 @@
          * {@link ContentResolver#applyBatch}), and if the {@link ContentValues} doesn't contain
          * this column, the voicemail provider implicitly sets it to 0 if the calling package is
          * the {@link #SOURCE_PACKAGE} or to 1 otherwise. To prevent this behavior, explicitly set
-         * {@link #DIRTY_RETAIN} to this column in the {@link ContentValues}.
+         * {@link #DIRTY_RETAIN} to DIRTY in the {@link ContentValues}.
          *
          * <P>Type: INTEGER (boolean)</P>
          *
diff --git a/core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl b/core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl
new file mode 100644
index 0000000..fe13179
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/KeyDerivationParameters.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.recoverablekeystore;
+
+/* @hide */
+parcelable KeyDerivationParameters;
diff --git a/core/java/android/security/recoverablekeystore/KeyDerivationParameters.java b/core/java/android/security/recoverablekeystore/KeyDerivationParameters.java
new file mode 100644
index 0000000..2205c41
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/KeyDerivationParameters.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.recoverablekeystore;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Collection of parameters which define a key derivation function.
+ * Supports
+ *
+ * <ul>
+ * <li>SHA256
+ * <li>Argon2id
+ * </ul>
+ * @hide
+ */
+public final class KeyDerivationParameters implements Parcelable {
+    private final int mAlgorithm;
+    private byte[] mSalt;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({ALGORITHM_SHA256, ALGORITHM_ARGON2ID})
+    public @interface KeyDerivationAlgorithm {
+    }
+
+    /**
+     * Salted SHA256
+     */
+    public static final int ALGORITHM_SHA256 = 1;
+
+    /**
+     * Argon2ID
+     */
+    // TODO: add Argon2ID support.
+    public static final int ALGORITHM_ARGON2ID = 2;
+
+    /**
+     * Creates instance of the class to to derive key using salted SHA256 hash.
+     */
+    public KeyDerivationParameters createSHA256Parameters(@NonNull byte[] salt) {
+        return new KeyDerivationParameters(ALGORITHM_SHA256, salt);
+    }
+
+    private KeyDerivationParameters(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt) {
+        mAlgorithm = algorithm;
+        mSalt = Preconditions.checkNotNull(salt);
+    }
+
+    /**
+     * Gets algorithm.
+     */
+    public @KeyDerivationAlgorithm int getAlgorithm() {
+        return mAlgorithm;
+    }
+
+    /**
+     * Gets salt.
+     */
+    public @NonNull byte[] getSalt() {
+        return mSalt;
+    }
+
+    public static final Parcelable.Creator<KeyDerivationParameters> CREATOR =
+            new Parcelable.Creator<KeyDerivationParameters>() {
+        public KeyDerivationParameters createFromParcel(Parcel in) {
+                return new KeyDerivationParameters(in);
+        }
+
+        public KeyDerivationParameters[] newArray(int length) {
+            return new KeyDerivationParameters[length];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mAlgorithm);
+        out.writeByteArray(mSalt);
+    }
+
+    protected KeyDerivationParameters(Parcel in) {
+        mAlgorithm = in.readInt();
+        mSalt = in.createByteArray();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.aidl b/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.aidl
new file mode 100644
index 0000000..1058463a
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore.recoverablekeystore;
+
+/* @hide */
+parcelable KeyEntryRecoveryData;
diff --git a/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.java b/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.java
new file mode 100644
index 0000000..80f5aa7
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/KeyEntryRecoveryData.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.recoverablekeystore;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+
+/**
+ * Helper class with data necessary recover a single application key, given a recovery key.
+ *
+ * <ul>
+ * <li>Alias - Keystore alias of the key.
+ * <li>Encrypted key material.
+ * </ul>
+ *
+ * Note that Application info is not included. Recovery Agent can only make its own keys
+ * recoverable.
+ *
+ * @hide
+ */
+public final class KeyEntryRecoveryData implements Parcelable {
+    private final byte[] mAlias;
+    // The only supported format is AES-256 symmetric key.
+    private final byte[] mEncryptedKeyMaterial;
+
+    public KeyEntryRecoveryData(@NonNull byte[] alias, @NonNull byte[] encryptedKeyMaterial) {
+        mAlias = Preconditions.checkNotNull(alias);
+        mEncryptedKeyMaterial = Preconditions.checkNotNull(encryptedKeyMaterial);
+    }
+
+    /**
+     * Application-specific alias of the key.
+     * @see java.security.KeyStore.aliases
+     */
+    public @NonNull byte[] getAlias() {
+        return mAlias;
+    }
+
+    /**
+     * Encrypted key material encrypted by recovery key.
+     */
+    public @NonNull byte[] getEncryptedKeyMaterial() {
+        return mEncryptedKeyMaterial;
+    }
+
+    public static final Parcelable.Creator<KeyEntryRecoveryData> CREATOR =
+            new Parcelable.Creator<KeyEntryRecoveryData>() {
+        public KeyEntryRecoveryData createFromParcel(Parcel in) {
+                return new KeyEntryRecoveryData(in);
+        }
+
+        public KeyEntryRecoveryData[] newArray(int length) {
+            return new KeyEntryRecoveryData[length];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeByteArray(mAlias);
+        out.writeByteArray(mEncryptedKeyMaterial);
+    }
+
+    protected KeyEntryRecoveryData(Parcel in) {
+        mAlias = in.createByteArray();
+        mEncryptedKeyMaterial = in.createByteArray();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl
new file mode 100644
index 0000000..bd76051
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.recoverablekeystore;
+
+/* @hide */
+parcelable KeyStoreRecoveryData;
diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.java b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.java
new file mode 100644
index 0000000..087f7a2
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryData.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.recoverablekeystore;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+
+/**
+ * Helper class which returns data necessary to recover keys.
+ * Contains
+ *
+ * <ul>
+ * <li>Snapshot version.
+ * <li>Recovery metadata with UI and key derivation parameters.
+ * <li>List of application keys encrypted by recovery key.
+ * <li>Encrypted recovery key.
+ * </ul>
+ *
+ * @hide
+ */
+public final class KeyStoreRecoveryData implements Parcelable {
+    private final int mSnapshotVersion;
+    private final List<KeyStoreRecoveryMetadata> mRecoveryMetadata;
+    private final List<KeyEntryRecoveryData> mApplicationKeyBlobs;
+    private final byte[] mEncryptedRecoveryKeyBlob;
+
+    public KeyStoreRecoveryData(int snapshotVersion, @NonNull List<KeyStoreRecoveryMetadata>
+            recoveryMetadata, @NonNull List<KeyEntryRecoveryData> applicationKeyBlobs,
+            @NonNull byte[] encryptedRecoveryKeyBlob) {
+        mSnapshotVersion = snapshotVersion;
+        mRecoveryMetadata = Preconditions.checkNotNull(recoveryMetadata);
+        mApplicationKeyBlobs = Preconditions.checkNotNull(applicationKeyBlobs);
+        mEncryptedRecoveryKeyBlob = Preconditions.checkNotNull(encryptedRecoveryKeyBlob);
+    }
+
+    /**
+     * Snapshot version for given account. It is incremented when user secret or list of application
+     * keys changes.
+     */
+    public int getSnapshotVersion() {
+        return mSnapshotVersion;
+    }
+
+    /**
+     * UI and key derivation parameters. Note that combination of secrets may be used.
+     */
+    public @NonNull List<KeyStoreRecoveryMetadata> getRecoveryMetadata() {
+        return mRecoveryMetadata;
+    }
+
+    /**
+     * List of application keys, with key material encrypted by
+     * the recovery key ({@link #getEncryptedRecoveryKeyBlob}).
+     */
+    public @NonNull List<KeyEntryRecoveryData> getApplicationKeyBlobs() {
+        return mApplicationKeyBlobs;
+    }
+
+    /**
+     * Recovery key blob, encrypted by user secret and recovery service public key.
+     */
+    public @NonNull byte[] getEncryptedRecoveryKeyBlob() {
+        return mEncryptedRecoveryKeyBlob;
+    }
+
+    public static final Parcelable.Creator<KeyStoreRecoveryData> CREATOR =
+            new Parcelable.Creator<KeyStoreRecoveryData>() {
+        public KeyStoreRecoveryData createFromParcel(Parcel in) {
+            return new KeyStoreRecoveryData(in);
+        }
+
+        public KeyStoreRecoveryData[] newArray(int length) {
+            return new KeyStoreRecoveryData[length];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mSnapshotVersion);
+        out.writeTypedList(mRecoveryMetadata);
+        out.writeByteArray(mEncryptedRecoveryKeyBlob);
+        out.writeTypedList(mApplicationKeyBlobs);
+    }
+
+    protected KeyStoreRecoveryData(Parcel in) {
+        mSnapshotVersion = in.readInt();
+        mRecoveryMetadata = in.createTypedArrayList(KeyStoreRecoveryMetadata.CREATOR);
+        mEncryptedRecoveryKeyBlob = in.createByteArray();
+        mApplicationKeyBlobs = in.createTypedArrayList(KeyEntryRecoveryData.CREATOR);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.aidl b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.aidl
new file mode 100644
index 0000000..e1d49de
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.recoverablekeystore;
+
+/* @hide */
+parcelable KeyStoreRecoveryMetadata;
diff --git a/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.java b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.java
new file mode 100644
index 0000000..43f9c80
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/KeyStoreRecoveryMetadata.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.recoverablekeystore;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+
+/**
+ * Helper class with data necessary to recover Keystore on a new device.
+ * It defines UI shown to the user and a way to derive a cryptographic key from user output.
+ *
+ * @hide
+ */
+public final class KeyStoreRecoveryMetadata implements Parcelable {
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({TYPE_LOCKSCREEN, TYPE_CUSTOM_PASSWORD})
+    public @interface UserSecretType {
+    }
+
+    /**
+     * Lockscreen secret is required to recover KeyStore.
+     */
+    public static final int TYPE_LOCKSCREEN = 1;
+
+    /**
+     * Custom passphrase, unrelated to lock screen, is required to recover KeyStore.
+     */
+    public static final int TYPE_CUSTOM_PASSWORD = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({TYPE_PIN, TYPE_PASSWORD, TYPE_PATTERN})
+    public @interface LockScreenUiFormat {
+    }
+
+    /**
+     * Pin with digits only.
+     */
+    public static final int TYPE_PIN = 1;
+
+    /**
+     * Password. String with latin-1 characters only.
+     */
+    public static final int TYPE_PASSWORD = 2;
+
+    /**
+     * Pattern with 3 by 3 grid.
+     */
+    public static final int TYPE_PATTERN = 3;
+
+    @UserSecretType
+    private final int mUserSecretType;
+
+    @LockScreenUiFormat
+    private final int mLockScreenUiFormat;
+
+    /**
+     * Parameters of key derivation function, including algorithm, difficulty, salt.
+     */
+    private KeyDerivationParameters mKeyDerivationParameters;
+    private byte[] mSecret; // Derived from user secret. The field must have limited visibility.
+
+    /**
+     * @param secret Constructor creates a reference to the secret. Caller must use
+     * @link {#clearSecret} to overwrite its value in memory.
+     */
+    public KeyStoreRecoveryMetadata(@UserSecretType int userSecretType,
+            @LockScreenUiFormat int lockScreenUiFormat,
+            @NonNull KeyDerivationParameters keyDerivationParameters, @NonNull byte[] secret) {
+        mUserSecretType = userSecretType;
+        mLockScreenUiFormat = lockScreenUiFormat;
+        mKeyDerivationParameters = Preconditions.checkNotNull(keyDerivationParameters);
+        mSecret = Preconditions.checkNotNull(secret);
+    }
+
+    /**
+     * Specifies UX shown to user during recovery.
+     *
+     * @see KeyStore.TYPE_PIN
+     * @see KeyStore.TYPE_PASSWORD
+     * @see KeyStore.TYPE_PATTERN
+     */
+    public @LockScreenUiFormat int getLockScreenUiFormat() {
+        return mLockScreenUiFormat;
+    }
+
+    /**
+     * Specifies function used to derive symmetric key from user input
+     * Format is defined in separate util class.
+     */
+    public @NonNull KeyDerivationParameters getKeyDerivationParameters() {
+        return mKeyDerivationParameters;
+    }
+
+    /**
+     * Secret string derived from user input.
+     */
+    public @NonNull byte[] getSecret() {
+        return mSecret;
+    }
+
+    /**
+     * @see KeyStore.TYPE_LOCKSCREEN
+     * @see KeyStore.TYPE_CUSTOM_PASSWORD
+     */
+    public @UserSecretType int getUserSecretType() {
+        return mUserSecretType;
+    }
+
+    /**
+     * Removes secret from memory than object is no longer used.
+     * Since finalizer call is not reliable, please use @link {#clearSecret} directly.
+     */
+    @Override
+    protected void finalize() throws Throwable {
+        clearSecret();
+        super.finalize();
+    }
+
+    /**
+     * Fills mSecret with zeroes.
+     */
+    public void clearSecret() {
+        Arrays.fill(mSecret, (byte) 0);
+    }
+
+    public static final Parcelable.Creator<KeyStoreRecoveryMetadata> CREATOR =
+            new Parcelable.Creator<KeyStoreRecoveryMetadata>() {
+        public KeyStoreRecoveryMetadata createFromParcel(Parcel in) {
+            return new KeyStoreRecoveryMetadata(in);
+        }
+
+        public KeyStoreRecoveryMetadata[] newArray(int length) {
+            return new KeyStoreRecoveryMetadata[length];
+        }
+    };
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mUserSecretType);
+        out.writeInt(mLockScreenUiFormat);
+        out.writeTypedObject(mKeyDerivationParameters, flags);
+        out.writeByteArray(mSecret);
+    }
+
+    protected KeyStoreRecoveryMetadata(Parcel in) {
+        mUserSecretType = in.readInt();
+        mLockScreenUiFormat = in.readInt();
+        mKeyDerivationParameters = in.readTypedObject(KeyDerivationParameters.CREATOR);
+        mSecret = in.createByteArray();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java b/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java
new file mode 100644
index 0000000..0510320
--- /dev/null
+++ b/core/java/android/security/recoverablekeystore/RecoverableKeyStoreLoader.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.recoverablekeystore;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.ServiceManager;
+
+import com.android.internal.widget.ILockSettings;
+
+import java.util.List;
+
+/**
+ * A wrapper around KeyStore which lets key be exported to
+ * trusted hardware on server side and recovered later.
+ *
+ * @hide
+ */
+public class RecoverableKeyStoreLoader  {
+
+    private final ILockSettings mBinder;
+
+    // Exception codes, should be in sync with {@code KeyStoreException}.
+    public static final int SYSTEM_ERROR = 4;
+
+    public static final int UNINITIALIZED_RECOVERY_PUBLIC_KEY = 20;
+
+    // Too many updates to recovery public key or server parameters.
+    public static final int RATE_LIMIT_EXCEEDED = 21;
+
+    private RecoverableKeyStoreLoader(ILockSettings binder) {
+        mBinder = binder;
+    }
+
+    /**
+     * @hide
+     */
+    public static RecoverableKeyStoreLoader getInstance() {
+        ILockSettings lockSettings =
+                ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"));
+        return new RecoverableKeyStoreLoader(lockSettings);
+    }
+
+    /**
+     * @hide
+     */
+    public static class RecoverableKeyStoreLoaderException extends Exception {
+        private final int mErrorCode;
+
+        public RecoverableKeyStoreLoaderException(int errorCode, String message) {
+            super(message);
+            mErrorCode = errorCode;
+        }
+
+        public int getErrorCode() {
+            return mErrorCode;
+        }
+    }
+
+    /**
+     * Initializes key recovery service for the calling application. RecoverableKeyStoreLoader
+     * randomly chooses one of the keys from the list
+     * and keeps it to use for future key export operations. Collection of all keys
+     * in the list must be signed by the provided {@code rootCertificateAlias}, which must also be
+     * present in the list of root certificates preinstalled on the device. The random selection
+     * allows RecoverableKeyStoreLoader to select which of a set of remote recovery service
+     * devices will be used.
+     *
+     * <p>In addition, RecoverableKeyStoreLoader enforces a delay of three months between
+     * consecutive initialization attempts, to limit the ability of an attacker to often switch
+     * remote recovery devices and significantly increase number of recovery attempts.
+     *
+     * @param rootCertificateAlias alias of a root certificate preinstalled on the device
+     * @param signedPublicKeyList binary blob a list of X509 certificates and signature
+     * @throws RecoverableKeyStoreLoaderException if signature is invalid, or key rotation was rate
+     * limited.
+     * @hide
+     */
+    public void initRecoveryService(@NonNull String rootCertificateAlias,
+            @NonNull byte[] signedPublicKeyList)
+            throws RecoverableKeyStoreLoaderException {
+        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+        // TODO: extend widget/ILockSettings.aidl
+        /* try {
+            mBinder.initRecoveryService(rootCertificate, publicKeyList);
+        } catch (RemoteException  e) {
+            throw e.rethrowFromSystemServer();
+        } */
+    }
+
+    /**
+     * Returns data necessary to store all recoverable keys for given account.
+     * Key material is encrypted with user secret and recovery public key.
+     */
+    public KeyStoreRecoveryData getRecoveryData(@NonNull byte[] account)
+            throws RecoverableKeyStoreLoaderException {
+        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+    }
+
+    /**
+     * Server parameters used to generate new recovery key blobs. This value will be included in
+     * {@code KeyStoreRecoveryData.getEncryptedRecoveryKeyBlob()}.
+     * The same value must be included in vaultParams  {@link startRecoverySession}
+     *
+     * @see #getRecoveryData
+     * @throws RecoverableKeyStoreLoaderException If parameters rotation is rate limited.
+     */
+    public void updateServerParameters(long serverParameters)
+            throws RecoverableKeyStoreLoaderException {
+        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+    }
+
+    /**
+     * Updates recovery status for given keys.
+     * It is used to notify keystore that key was successfully stored on the server or
+     * there were an error. Returned as a part of KeyInfo data structure.
+     *
+     * @param packageName Application whose recoverable keys' statuses are to be updated.
+     * @param aliases List of application-specific key aliases. If the array is empty, updates the
+     * status for all existing recoverable keys.
+     * @param status Status specific to recovery agent.
+     */
+    public void setRecoveryStatus(@NonNull String packageName, @Nullable String[] aliases,
+            int status) throws NameNotFoundException, RecoverableKeyStoreLoaderException {
+        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+    }
+
+    /**
+     * Specifies a set of secret types used for end-to-end keystore encryption.
+     * Knowing all of them is necessary to recover data.
+     *
+     * @param secretTypes {@link KeyStoreRecoveryMetadata#TYPE_LOCKSCREEN} or
+     * {@link KeyStoreRecoveryMetadata#TYPE_CUSTOM_PASSWORD}
+     */
+    public void setRecoverySecretTypes(@NonNull @KeyStoreRecoveryMetadata.UserSecretType
+            int[] secretTypes) throws RecoverableKeyStoreLoaderException {
+        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+    }
+
+    /**
+     * Defines a set of secret types used for end-to-end keystore encryption.
+     * Knowing all of them is necessary to generate KeyStoreRecoveryData.
+     * @see KeyStoreRecoveryData
+     */
+    public @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] getRecoverySecretTypes()
+            throws RecoverableKeyStoreLoaderException {
+        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+    }
+
+    /**
+     * Returns a list of recovery secret types, necessary to create a pending recovery snapshot.
+     * When user enters a secret of a pending type
+     * {@link #recoverySecretAvailable} should be called.
+     */
+    public @NonNull @KeyStoreRecoveryMetadata.UserSecretType int[] getPendingRecoverySecretTypes()
+            throws RecoverableKeyStoreLoaderException {
+        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+    }
+
+    /**
+     * Method notifies KeyStore that a user-generated secret is available.
+     * This method generates a symmetric session key which a trusted remote device can use
+     * to return a recovery key.
+     * Caller should use {@link KeyStoreRecoveryMetadata#clearSecret} to override the secret value
+     * in memory.
+     *
+     * @param recoverySecret user generated secret together with parameters necessary to
+     * regenerate it on a new device.
+     */
+    public void recoverySecretAvailable(@NonNull KeyStoreRecoveryMetadata recoverySecret)
+            throws RecoverableKeyStoreLoaderException {
+        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+    }
+
+    /**
+     * Initializes recovery session and returns a blob with proof of recovery secrets possession.
+     * The method generates symmetric key for a session, which trusted remote device can use
+     * to return recovery key.
+     *
+     * @param sessionId ID for recovery session.
+     * @param verifierPublicKey Certificate with Public key used to create the recovery blob on
+     * the source device. Keystore will verify the certificate using root of trust.
+     * @param vaultParams Must match the parameters in the corresponding field in the recovery blob.
+     * Used to limit number of guesses.
+     * @param vaultChallenge Data passed from server for this recovery session and used to prevent
+     * replay attacks
+     * @param secrets Secrets provided by user, the method only uses type and secret fields.
+     * @return Binary blob with recovery claim. It is encrypted with verifierPublicKey and
+     * contains a proof of user secrets, session symmetric key and parameters necessary to identify
+     * the counter with the number of failed recovery attempts.
+     */
+    public @NonNull byte[] startRecoverySession(@NonNull String sessionId,
+            @NonNull byte[] verifierPublicKey, @NonNull byte[] vaultParams,
+            @NonNull byte[] vaultChallenge, @NonNull List<KeyStoreRecoveryMetadata> secrets)
+            throws RecoverableKeyStoreLoaderException {
+        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+    }
+
+    /**
+     * Imports keys.
+     *
+     * @param sessionId Id for recovery session, same as in = {@link startRecoverySession}.
+     * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session.
+     * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob
+     * and session. KeyStore only uses package names from the application info in
+     * {@link KeyEntryRecoveryData}. Caller is responsibility to perform certificates check.
+     */
+    public void recoverKeys(@NonNull String sessionId, @NonNull byte[] recoveryKeyBlob,
+            @NonNull List<KeyEntryRecoveryData> applicationKeys)
+            throws RecoverableKeyStoreLoaderException {
+        throw new RecoverableKeyStoreLoaderException(SYSTEM_ERROR, "Not implemented");
+    }
+}
diff --git a/core/java/android/service/autofill/AutofillService.java b/core/java/android/service/autofill/AutofillService.java
index 97fdcef..2600f8a 100644
--- a/core/java/android/service/autofill/AutofillService.java
+++ b/core/java/android/service/autofill/AutofillService.java
@@ -453,13 +453,46 @@
  * email address), the service should only use it locally (i.e., in the app's process) for
  * heuristics purposes, but it should not be sent to external servers.
  *
- * <a name="FieldsClassification"></a>
- * <h3>Metrics and fields classification</h3
+ * <a name="FieldClassification"></a>
+ * <h3>Metrics and field classification</h3
  *
- * <p>TODO(b/67867469): document it or remove this section; in particular, document the relationship
- * between set/getUserData(), FillResponse.setFieldClassificationIds(), and
- * FillEventHistory.getFieldsClassification.
+ * <p>The service can call {@link #getFillEventHistory()} to get metrics representing the user
+ * actions, and then use these metrics to improve its heuristics.
+ *
+ * <p>Prior to Android {@link android.os.Build.VERSION_CODES#P}, the metrics covered just the
+ * scenarios where the service knew how to autofill an activity, but Android
+ * {@link android.os.Build.VERSION_CODES#P} introduced a new mechanism called field classification,
+ * which allows the service to dinamically classify the meaning of fields based on the existing user
+ * data known by the service.
+ *
+ * <p>Typically, field classification can be used to detect fields that can be autofilled with
+ * user data that is not associated with a specific app&mdash;such as email and physical
+ * address. Once the service identifies that a such field was manually filled by the user, the
+ * service could use this signal to improve its heuristics, either locally (i.e., in the same
+ * device) or globally (i.e., by crowdsourcing the results back to the service's server so it can
+ * be used by other users).
+ *
+ * <p>The field classification workflow involves 4 steps:
+ *
+ * <ol>
+ *   <li>Set the user data through {@link AutofillManager#setUserData(UserData)}. This data is
+ *   cached until the system restarts (or the service is disabled), so it doesn't need to be set for
+ *   all requests.
+ *   <li>Identify which fields should be analysed by calling
+ *   {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)}.
+ *   <li>Verify the results through {@link FillEventHistory.Event#getFieldsClassification()}.
+ *   <li>Use the results to dynamically create {@link Dataset} or {@link SaveInfo} objects in future
+ *   requests.
+ * </ol>
+ *
+ * <p>The field classification is an expensive operation and should be used carefully, otherwise it
+ * can reach its rate limit and get blocked by the Android System. Ideally, it should be used just
+ * in cases where the service could not determine how an activity can be autofilled, but it has a
+ * strong suspicious that it could. For example, if an activity has four or more fields and one of
+ * them is a list, chances are that these are address fields (like address, city, state, and
+ * zip code).
  */
+// TODO(b/70407264): add code snippets above???
 public abstract class AutofillService extends Service {
     private static final String TAG = "AutofillService";
 
diff --git a/core/java/android/service/autofill/EditDistanceScorer.java b/core/java/android/service/autofill/EditDistanceScorer.java
index e25cd04..0706b37 100644
--- a/core/java/android/service/autofill/EditDistanceScorer.java
+++ b/core/java/android/service/autofill/EditDistanceScorer.java
@@ -16,7 +16,6 @@
 package android.service.autofill;
 
 import android.annotation.NonNull;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.autofill.AutofillValue;
@@ -24,14 +23,8 @@
 /**
  * Helper used to calculate the classification score between an actual {@link AutofillValue} filled
  * by the user and the expected value predicted by an autofill service.
- *
- * TODO(b/67867469):
- * - improve javadoc
- * - document algorithm / copy from InternalScorer
- * - unhide / remove testApi
- * @hide
  */
-@TestApi
+// TODO(b/70291841): explain algorithm once it's fully implemented
 public final class EditDistanceScorer extends InternalScorer implements Scorer, Parcelable {
 
     private static final EditDistanceScorer sInstance = new EditDistanceScorer();
@@ -46,10 +39,11 @@
     private EditDistanceScorer() {
     }
 
+    /** @hide */
     @Override
     public float getScore(@NonNull AutofillValue actualValue, @NonNull String userData) {
         if (actualValue == null || !actualValue.isText() || userData == null) return 0;
-        // TODO(b/67867469): implement edit distance - currently it's returning either 0, 100%, or
+        // TODO(b/70291841): implement edit distance - currently it's returning either 0, 100%, or
         // partial match when number of chars match
         final String textValue = actualValue.getTextValue().toString();
         final int total = textValue.length();
diff --git a/core/java/android/service/autofill/FieldClassification.java b/core/java/android/service/autofill/FieldClassification.java
index 0a60208..b28c6f8 100644
--- a/core/java/android/service/autofill/FieldClassification.java
+++ b/core/java/android/service/autofill/FieldClassification.java
@@ -19,7 +19,6 @@
 import static android.view.autofill.Helper.sDebug;
 
 import android.annotation.NonNull;
-import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.view.autofill.Helper;
@@ -31,15 +30,10 @@
 import java.util.List;
 
 /**
- * Gets the <a href="#FieldsClassification">fields classification</a> results for a given field.
- *
- * TODO(b/67867469):
- * - improve javadoc
- * - unhide / remove testApi
- *
- * @hide
+ * Represents the <a href="AutofillService.html#FieldClassification">field classification</a>
+ * results for a given field.
  */
-@TestApi
+// TODO(b/70291841): let caller handle Parcelable...
 public final class FieldClassification implements Parcelable {
 
     private final Match mMatch;
@@ -79,7 +73,7 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeParcelable(mMatch, flags);
+        mMatch.writeToParcel(parcel);
     }
 
     public static final Parcelable.Creator<FieldClassification> CREATOR =
@@ -87,7 +81,7 @@
 
         @Override
         public FieldClassification createFromParcel(Parcel parcel) {
-            return new FieldClassification(parcel.readParcelable(null));
+            return new FieldClassification(Match.readFromParcel(parcel));
         }
 
         @Override
@@ -97,16 +91,12 @@
     };
 
     /**
-     * Gets the score of a {@link UserData} entry for the field.
+     * Represents the score of a {@link UserData} entry for the field.
      *
-     * TODO(b/67867469):
-     * - improve javadoc
-     * - unhide / remove testApi
-     *
-     * @hide
+     * <p>The score is defined by {@link #getScore()} and the entry is identified by
+     * {@link #getRemoteId()}.
      */
-    @TestApi
-    public static final class Match implements Parcelable {
+    public static final class Match {
 
         private final String mRemoteId;
         private final float mScore;
@@ -126,26 +116,19 @@
         }
 
         /**
-         * Gets a score between the value of this field and the value of the {@link UserData} entry.
+         * Gets a classification score for the value of this field compared to the value of the
+         * {@link UserData} entry.
          *
-         * <p>The score is based in a case-insensitive comparisson of all characters from both the
-         * field value and the user data entry, and it ranges from {@code 0} to {@code 1000000}:
+         * <p>The score is based in a comparison of the field value and the user data entry, and it
+         * ranges from {@code 0.0F} to {@code 1.0F}:
          * <ul>
-         *   <li>{@code 1.0} represents a full match ({@code 100%}).
-         *   <li>{@code 0.0} represents a full mismatch ({@code 0%}).
+         *   <li>{@code 1.0F} represents a full match ({@code 100%}).
+         *   <li>{@code 0.0F} represents a full mismatch ({@code 0%}).
          *   <li>Any other value is a partial match.
          * </ul>
          *
-         * <p>How the score is calculated depends on the algorithm used by the Android System.
-         * For example, if the user  data is {@code "abc"} and the field value us {@code " abc"},
-         * the result could be:
-         * <ul>
-         *   <li>{@code 1.0} if the algorithm trims the values.
-         *   <li>{@code 0.0} if the algorithm compares the values sequentially.
-         *   <li>{@code 0.75} if the algorithm consideres that 3/4 (75%) of the characters match.
-         * </ul>
-         *
-         * <p>Currently, the autofill service cannot configure the algorithm.
+         * <p>How the score is calculated depends on the algorithm used by the {@link Scorer}
+         * implementation.
          */
         public float getScore() {
             return mScore;
@@ -160,33 +143,31 @@
             return string.append(", score=").append(mScore).toString();
         }
 
-        /////////////////////////////////////
-        // Parcelable "contract" methods. //
-        /////////////////////////////////////
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel parcel, int flags) {
+        private void writeToParcel(@NonNull Parcel parcel) {
             parcel.writeString(mRemoteId);
             parcel.writeFloat(mScore);
         }
 
-        @SuppressWarnings("hiding")
-        public static final Parcelable.Creator<Match> CREATOR = new Parcelable.Creator<Match>() {
+        private static Match readFromParcel(@NonNull Parcel parcel) {
+            return new Match(parcel.readString(), parcel.readFloat());
+        }
 
-            @Override
-            public Match createFromParcel(Parcel parcel) {
-                return new Match(parcel.readString(), parcel.readFloat());
+        /** @hide */
+        public static Match[] readArrayFromParcel(@NonNull Parcel parcel) {
+            final int length = parcel.readInt();
+            final Match[] matches = new Match[length];
+            for (int i = 0; i < length; i++) {
+                matches[i] = readFromParcel(parcel);
             }
+            return matches;
+        }
 
-            @Override
-            public Match[] newArray(int size) {
-                return new Match[size];
+        /** @hide */
+        public static void writeArrayToParcel(@NonNull Parcel parcel, @NonNull Match[] matches) {
+            parcel.writeInt(matches.length);
+            for (int i = 0; i < matches.length; i++) {
+                matches[i].writeToParcel(parcel);
             }
-        };
+        }
     }
 }
diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java
index a527f16..07fab61 100644
--- a/core/java/android/service/autofill/FillEventHistory.java
+++ b/core/java/android/service/autofill/FillEventHistory.java
@@ -21,7 +21,6 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.TestApi;
 import android.content.IntentSender;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -158,7 +157,7 @@
                 final AutofillId[] detectedFields = event.mDetectedFieldIds;
                 parcel.writeParcelableArray(detectedFields, flags);
                 if (detectedFields != null) {
-                    parcel.writeParcelableArray(event.mDetectedMatches, flags);
+                    Match.writeArrayToParcel(parcel, event.mDetectedMatches);
                 }
             }
         }
@@ -208,6 +207,7 @@
          *       ({@link #getIgnoredDatasetIds()}).
          *   <li>Which fields in the selected datasets were changed by the user after the dataset
          *       was selected ({@link #getChangedFields()}.
+         *   <li>Which fields match the {@link UserData} set by the service.
          * </ul>
          *
          * <p><b>Note: </b>This event is only generated when:
@@ -222,7 +222,6 @@
          * <p>See {@link android.view.autofill.AutofillManager} for more information about autofill
          * contexts.
          */
-        // TODO(b/67867469): update with field detection behavior
         public static final int TYPE_CONTEXT_COMMITTED = 4;
 
         /** @hide */
@@ -356,19 +355,13 @@
         }
 
         /**
-         * Gets the <a href="#FieldsClassification">fields classification</a> results.
+         * Gets the <a href="AutofillService.html#FieldClassification">field classification</a>
+         * results.
          *
          * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}, when the
          * service requested {@link FillResponse.Builder#setFieldClassificationIds(AutofillId...)
-         * fields classification}.
-         *
-         * TODO(b/67867469):
-         *  - improve javadoc
-         *  - unhide / remove testApi
-         *
-         * @hide
+         * field classification}.
          */
-        @TestApi
         @NonNull public Map<AutofillId, FieldClassification> getFieldsClassification() {
             if (mDetectedFieldIds == null) {
                 return Collections.emptyMap();
@@ -462,6 +455,8 @@
          * and belonged to datasets.
          * @param manuallyFilledDatasetIds The ids of datasets that had values matching the
          * respective entry on {@code manuallyFilledFieldIds}.
+         * @param detectedMatches the field classification matches.
+         *
          * @throws IllegalArgumentException If the length of {@code changedFieldIds} and
          * {@code changedDatasetIds} doesn't match.
          * @throws IllegalArgumentException If the length of {@code manuallyFilledFieldIds} and
@@ -469,7 +464,6 @@
          *
          * @hide
          */
-        // TODO(b/67867469): document field classification parameters once stable
         public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState,
                 @Nullable List<String> selectedDatasetIds,
                 @Nullable ArraySet<String> ignoredDatasetIds,
@@ -477,7 +471,7 @@
                 @Nullable ArrayList<String> changedDatasetIds,
                 @Nullable ArrayList<AutofillId> manuallyFilledFieldIds,
                 @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,
-                @Nullable AutofillId[] detectedFieldIds, @Nullable Match[] detectedMaches) {
+                @Nullable AutofillId[] detectedFieldIds, @Nullable Match[] detectedMatches) {
             mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_CONTEXT_COMMITTED,
                     "eventType");
             mDatasetId = datasetId;
@@ -502,7 +496,7 @@
             mManuallyFilledDatasetIds = manuallyFilledDatasetIds;
 
             mDetectedFieldIds = detectedFieldIds;
-            mDetectedMatches = detectedMaches;
+            mDetectedMatches = detectedMatches;
         }
 
         @Override
@@ -555,7 +549,7 @@
                         final AutofillId[] detectedFieldIds = parcel.readParcelableArray(null,
                                 AutofillId.class);
                         final Match[] detectedMatches = (detectedFieldIds != null)
-                                ? parcel.readParcelableArray(null, Match.class)
+                                ? Match.readArrayFromParcel(parcel)
                                 : null;
 
                         selection.addEvent(new Event(eventType, datasetId, clientState,
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 014d3e8..3a4b6bb 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -41,7 +41,7 @@
 import java.util.List;
 
 /**
- * Response for a {@link
+ * Response for an {@link
  * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}.
  *
  * <p>See the main {@link AutofillService} documentation for more details and examples.
@@ -156,6 +156,7 @@
     }
 
     /** @hide */
+    @TestApi
     public int getFlags() {
         return mFlags;
     }
@@ -352,22 +353,18 @@
         }
 
         /**
-         * Sets which fields are used for <a href="#FieldsClassification">fields classification</a>
+         * Sets which fields are used for
+         * <a href="AutofillService.html#FieldClassification">field classification</a>
          *
+         * <p><b>Note:</b> This method automatically adds the
+         * {@link FillResponse#FLAG_TRACK_CONTEXT_COMMITED} to the {@link #setFlags(int) flags}.
+
          * @throws IllegalArgumentException is length of {@code ids} args is more than
          * {@link UserData#getMaxFieldClassificationIdsSize()}.
          * @throws IllegalStateException if {@link #build()} or {@link #disableAutofill(long)} was
          * already called.
          * @throws NullPointerException if {@code ids} or any element on it is {@code null}.
-         *
-         * TODO(b/67867469):
-         *  - improve javadoc: explain relationship with UserData and how to check results
-         *  - unhide / remove testApi
-         *  - implement multiple ids
-         *
-         * @hide
          */
-        @TestApi
         public Builder setFieldClassificationIds(@NonNull AutofillId... ids) {
             throwIfDestroyed();
             throwIfDisableAutofillCalled();
@@ -375,6 +372,7 @@
             Preconditions.checkArgumentInRange(ids.length, 1,
                     UserData.getMaxFieldClassificationIdsSize(), "ids length");
             mFieldClassificationIds = ids;
+            mFlags |= FLAG_TRACK_CONTEXT_COMMITED;
             return this;
         }
 
@@ -423,9 +421,8 @@
          * @throws IllegalStateException if either {@link #addDataset(Dataset)},
          *       {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
          *       {@link #setSaveInfo(SaveInfo)}, {@link #setClientState(Bundle)}, or
-         *       {link #setFieldClassificationIds(AutofillId...)} was already called.
+         *       {@link #setFieldClassificationIds(AutofillId...)} was already called.
          */
-        // TODO(b/67867469): add @ to {link setFieldClassificationIds} once it's public
         public Builder disableAutofill(long duration) {
             throwIfDestroyed();
             if (duration <= 0) {
@@ -506,14 +503,13 @@
          *       {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
          *       {@link #setSaveInfo(SaveInfo)}, {@link #disableAutofill(long)},
          *       {@link #setClientState(Bundle)},
-         *       or {link #setFieldClassificationIds(AutofillId...)}.
+         *       or {@link #setFieldClassificationIds(AutofillId...)}.
          *   <li>{@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} is called
          *       without any previous calls to {@link #addDataset(Dataset)}.
          * </ol>
          *
          * @return A built response.
          */
-        // TODO(b/67867469): add @ to {link setFieldClassificationIds} once it's public
         public FillResponse build() {
             throwIfDestroyed();
             if (mAuthentication == null && mDatasets == null && mSaveInfo == null
diff --git a/core/java/android/service/autofill/Scorer.java b/core/java/android/service/autofill/Scorer.java
index f6a802a..c401855 100644
--- a/core/java/android/service/autofill/Scorer.java
+++ b/core/java/android/service/autofill/Scorer.java
@@ -15,21 +15,14 @@
  */
 package android.service.autofill;
 
-import android.annotation.TestApi;
-
 /**
  * Helper class used to calculate a score.
  *
- * <p>Typically used to calculate the field classification score between an actual
- * {@link android.view.autofill.AutofillValue}  filled by the user and the expected value predicted
- * by an autofill service.
- *
- * TODO(b/67867469):
- * - improve javadoc
- * - unhide / remove testApi
- * @hide
+ * <p>Typically used to calculate the
+ * <a href="AutofillService.html#FieldClassification">field classification</a> score between an
+ * actual {@link android.view.autofill.AutofillValue} filled by the user and the expected value
+ * predicted by an autofill service.
  */
-@TestApi
 public interface Scorer {
 
 }
diff --git a/core/java/android/service/autofill/UserData.java b/core/java/android/service/autofill/UserData.java
index 0d37815..f0cc360 100644
--- a/core/java/android/service/autofill/UserData.java
+++ b/core/java/android/service/autofill/UserData.java
@@ -23,7 +23,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.annotation.TestApi;
 import android.app.ActivityThread;
 import android.content.ContentResolver;
 import android.os.Parcel;
@@ -38,15 +37,9 @@
 import java.util.ArrayList;
 
 /**
- * Class used by service to improve autofillable fields detection by tracking the meaning of fields
- * manually edited by the user (when they match values provided by the service).
- *
- * TODO(b/67867469):
- *  - improve javadoc / add link to section on AutofillService
- *  - unhide / remove testApi
- * @hide
+ * Defines the user data used for
+ * <a href="AutofillService.html#FieldClassification">field classification</a>.
  */
-@TestApi
 public final class UserData implements Parcelable {
 
     private static final String TAG = "UserData";
@@ -110,12 +103,7 @@
 
     /**
      * A builder for {@link UserData} objects.
-     *
-     * TODO(b/67867469): unhide / remove testApi
-     *
-     * @hide
      */
-    @TestApi
     public static final class Builder {
         private final InternalScorer mScorer;
         private final ArrayList<String> mRemoteIds;
@@ -123,7 +111,7 @@
         private boolean mDestroyed;
 
         /**
-         * Creates a new builder for the user data used for <a href="#FieldsClassification">fields
+         * Creates a new builder for the user data used for <a href="#FieldClassification">field
          * classification</a>.
          *
          * @throws IllegalArgumentException if any of the following occurs:
@@ -288,14 +276,16 @@
     }
 
     /**
-     * Gets the minimum length of values passed to {@link Builder#Builder(Scorer, String, String)}.
+     * Gets the minimum length of values passed to the builder's constructor or
+     * or {@link Builder#add(String, String)}.
      */
     public static int getMinValueLength() {
         return getInt(AUTOFILL_USER_DATA_MIN_VALUE_LENGTH, DEFAULT_MIN_VALUE_LENGTH);
     }
 
     /**
-     * Gets the maximum length of values passed to {@link Builder#Builder(Scorer, String, String)}.
+     * Gets the maximum length of values passed to the builder's constructor or
+     * or {@link Builder#add(String, String)}.
      */
     public static int getMaxValueLength() {
         return getInt(AUTOFILL_USER_DATA_MAX_VALUE_LENGTH, DEFAULT_MAX_VALUE_LENGTH);
diff --git a/core/java/android/service/carrier/CarrierService.java b/core/java/android/service/carrier/CarrierService.java
index 2707f14..b94ccf9 100644
--- a/core/java/android/service/carrier/CarrierService.java
+++ b/core/java/android/service/carrier/CarrierService.java
@@ -33,8 +33,8 @@
  * To extend this class, you must declare the service in your manifest file to require the
  * {@link android.Manifest.permission#BIND_CARRIER_SERVICES} permission and include an intent
  * filter with the {@link #CARRIER_SERVICE_INTERFACE}. If the service should have a long-lived
- * binding, set android.service.carrier.LONG_LIVED_BINDING to true in the service's metadata.
- * For example:
+ * binding, set <code>android.service.carrier.LONG_LIVED_BINDING</code> to <code>true</code> in the
+ * service's metadata. For example:
  * </p>
  *
  * <pre>{@code
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 54b48b6..b7d33599 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -41,7 +41,7 @@
         DEFAULT_FLAGS.put("new_settings_suggestion", "true");
         DEFAULT_FLAGS.put("settings_search_v2", "false");
         DEFAULT_FLAGS.put("settings_app_info_v2", "false");
-        DEFAULT_FLAGS.put("settings_connected_device_v2", "false");
+        DEFAULT_FLAGS.put("settings_connected_device_v2", "true");
         DEFAULT_FLAGS.put("settings_battery_v2", "false");
         DEFAULT_FLAGS.put("settings_battery_display_app_list", "false");
     }
diff --git a/core/java/android/util/StatsLog.java b/core/java/android/util/StatsLog.java
new file mode 100644
index 0000000..4805318
--- /dev/null
+++ b/core/java/android/util/StatsLog.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * StatsLog provides an API for developers to send events to statsd. The events can be used to
+ * define custom metrics inside statsd. We will rate-limit how often the calls can be made inside
+ * statsd.
+ */
+public final class StatsLog extends StatsLogInternal {
+    private static final String TAG = "StatsManager";
+
+    private StatsLog() {}
+
+    /**
+     * Logs a start event.
+     *
+     * @param label developer-chosen label that is from [0, 16).
+     * @return True if the log request was sent to statsd.
+     */
+    public static boolean logStart(int label) {
+        if (label >= 0 && label < 16) {
+            StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__START);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Logs a stop event.
+     *
+     * @param label developer-chosen label that is from [0, 16).
+     * @return True if the log request was sent to statsd.
+     */
+    public static boolean logStop(int label) {
+        if (label >= 0 && label < 16) {
+            StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__STOP);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Logs an event that does not represent a start or stop boundary.
+     *
+     * @param label developer-chosen label that is from [0, 16).
+     * @return True if the log request was sent to statsd.
+     */
+    public static boolean logEvent(int label) {
+        if (label >= 0 && label < 16) {
+            StatsLog.write(APP_HOOK, label, APP_HOOK__STATE__UNSPECIFIED);
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ab5372a..02beee0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4299,6 +4299,38 @@
          */
         Runnable mShowTooltipRunnable;
         Runnable mHideTooltipRunnable;
+
+        /**
+         * Hover move is ignored if it is within this distance in pixels from the previous one.
+         */
+        int mHoverSlop;
+
+        /**
+         * Update the anchor position if it significantly (that is by at least mHoverSlop)
+         * different from the previously stored position. Ignoring insignificant changes
+         * filters out the jitter which is typical for such input sources as stylus.
+         *
+         * @return True if the position has been updated.
+         */
+        private boolean updateAnchorPos(MotionEvent event) {
+            final int newAnchorX = (int) event.getX();
+            final int newAnchorY = (int) event.getY();
+            if (Math.abs(newAnchorX - mAnchorX) <= mHoverSlop
+                    && Math.abs(newAnchorY - mAnchorY) <= mHoverSlop) {
+                return false;
+            }
+            mAnchorX = newAnchorX;
+            mAnchorY = newAnchorY;
+            return true;
+        }
+
+        /**
+         *  Clear the anchor position to ensure that the next change is considered significant.
+         */
+        private void clearAnchorPos() {
+            mAnchorX = Integer.MAX_VALUE;
+            mAnchorY = Integer.MAX_VALUE;
+        }
     }
 
     TooltipInfo mTooltipInfo;
@@ -26816,6 +26848,8 @@
                 mTooltipInfo = new TooltipInfo();
                 mTooltipInfo.mShowTooltipRunnable = this::showHoverTooltip;
                 mTooltipInfo.mHideTooltipRunnable = this::hideTooltip;
+                mTooltipInfo.mHoverSlop = ViewConfiguration.get(mContext).getScaledHoverSlop();
+                mTooltipInfo.clearAnchorPos();
             }
             mTooltipInfo.mTooltipText = tooltipText;
         }
@@ -26882,6 +26916,7 @@
         mTooltipInfo.mTooltipPopup.hide();
         mTooltipInfo.mTooltipPopup = null;
         mTooltipInfo.mTooltipFromLongClick = false;
+        mTooltipInfo.clearAnchorPos();
         if (mAttachInfo != null) {
             mAttachInfo.mTooltipHost = null;
         }
@@ -26906,11 +26941,9 @@
                 if ((mViewFlags & TOOLTIP) != TOOLTIP || (mViewFlags & ENABLED_MASK) != ENABLED) {
                     break;
                 }
-                if (!mTooltipInfo.mTooltipFromLongClick) {
+                if (!mTooltipInfo.mTooltipFromLongClick && mTooltipInfo.updateAnchorPos(event)) {
                     if (mTooltipInfo.mTooltipPopup == null) {
                         // Schedule showing the tooltip after a timeout.
-                        mTooltipInfo.mAnchorX = (int) event.getX();
-                        mTooltipInfo.mAnchorY = (int) event.getY();
                         removeCallbacks(mTooltipInfo.mShowTooltipRunnable);
                         postDelayed(mTooltipInfo.mShowTooltipRunnable,
                                 ViewConfiguration.getHoverTooltipShowTimeout());
@@ -26932,6 +26965,7 @@
                 return true;
 
             case MotionEvent.ACTION_HOVER_EXIT:
+                mTooltipInfo.clearAnchorPos();
                 if (!mTooltipInfo.mTooltipFromLongClick) {
                     hideTooltip();
                 }
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index c44c8dd..c5a94da 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -290,6 +290,7 @@
     private final int mMaximumFlingVelocity;
     private final int mScrollbarSize;
     private final int mTouchSlop;
+    private final int mHoverSlop;
     private final int mMinScrollbarTouchTarget;
     private final int mDoubleTapTouchSlop;
     private final int mPagingTouchSlop;
@@ -320,6 +321,7 @@
         mMaximumFlingVelocity = MAXIMUM_FLING_VELOCITY;
         mScrollbarSize = SCROLL_BAR_SIZE;
         mTouchSlop = TOUCH_SLOP;
+        mHoverSlop = TOUCH_SLOP / 2;
         mMinScrollbarTouchTarget = MIN_SCROLLBAR_TOUCH_TARGET;
         mDoubleTapTouchSlop = DOUBLE_TAP_TOUCH_SLOP;
         mPagingTouchSlop = PAGING_TOUCH_SLOP;
@@ -407,6 +409,8 @@
                 com.android.internal.R.bool.config_ui_enableFadingMarquee);
         mTouchSlop = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.config_viewConfigurationTouchSlop);
+        mHoverSlop = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.config_viewConfigurationHoverSlop);
         mMinScrollbarTouchTarget = res.getDimensionPixelSize(
                 com.android.internal.R.dimen.config_minScrollbarTouchTarget);
         mPagingTouchSlop = mTouchSlop * 2;
@@ -640,6 +644,14 @@
     }
 
     /**
+     * @return Distance in pixels a hover can wander while it is still considered "stationary".
+     *
+     */
+    public int getScaledHoverSlop() {
+        return mHoverSlop;
+    }
+
+    /**
      * @return Distance in pixels the first touch can wander before we do not consider this a
      * potential double tap event
      * @hide
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index c6c42fa..3f8da093 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -324,6 +324,13 @@
     final Rect mTempRect; // used in the transaction to not thrash the heap.
     final Rect mVisRect; // used to retrieve visible rect of focused view.
 
+    // This is used to reduce the race between window focus changes being dispatched from
+    // the window manager and input events coming through the input system.
+    @GuardedBy("this")
+    boolean mUpcomingWindowFocus;
+    @GuardedBy("this")
+    boolean mUpcomingInTouchMode;
+
     public boolean mTraversalScheduled;
     int mTraversalBarrier;
     boolean mWillDrawSoon;
@@ -2461,6 +2468,93 @@
         }
     }
 
+    private void handleWindowFocusChanged() {
+        final boolean hasWindowFocus;
+        final boolean inTouchMode;
+        synchronized (this) {
+            hasWindowFocus = mUpcomingWindowFocus;
+            inTouchMode = mUpcomingInTouchMode;
+        }
+
+        if (mAttachInfo.mHasWindowFocus == hasWindowFocus) {
+            return;
+        }
+
+        if (mAdded) {
+            profileRendering(hasWindowFocus);
+
+            if (hasWindowFocus) {
+                ensureTouchModeLocally(inTouchMode);
+                if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) {
+                    mFullRedrawNeeded = true;
+                    try {
+                        final WindowManager.LayoutParams lp = mWindowAttributes;
+                        final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null;
+                        mAttachInfo.mThreadedRenderer.initializeIfNeeded(
+                                mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
+                    } catch (OutOfResourcesException e) {
+                        Log.e(mTag, "OutOfResourcesException locking surface", e);
+                        try {
+                            if (!mWindowSession.outOfMemory(mWindow)) {
+                                Slog.w(mTag, "No processes killed for memory;"
+                                        + " killing self");
+                                Process.killProcess(Process.myPid());
+                            }
+                        } catch (RemoteException ex) {
+                        }
+                        // Retry in a bit.
+                        mHandler.sendMessageDelayed(mHandler.obtainMessage(
+                                MSG_WINDOW_FOCUS_CHANGED), 500);
+                        return;
+                    }
+                }
+            }
+
+            mAttachInfo.mHasWindowFocus = hasWindowFocus;
+
+            mLastWasImTarget = WindowManager.LayoutParams
+                    .mayUseInputMethod(mWindowAttributes.flags);
+
+            InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
+                imm.onPreWindowFocus(mView, hasWindowFocus);
+            }
+            if (mView != null) {
+                mAttachInfo.mKeyDispatchState.reset();
+                mView.dispatchWindowFocusChanged(hasWindowFocus);
+                mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
+
+                if (mAttachInfo.mTooltipHost != null) {
+                    mAttachInfo.mTooltipHost.hideTooltip();
+                }
+            }
+
+            // Note: must be done after the focus change callbacks,
+            // so all of the view state is set up correctly.
+            if (hasWindowFocus) {
+                if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
+                    imm.onPostWindowFocus(mView, mView.findFocus(),
+                            mWindowAttributes.softInputMode,
+                            !mHasHadWindowFocus, mWindowAttributes.flags);
+                }
+                // Clear the forward bit.  We can just do this directly, since
+                // the window manager doesn't care about it.
+                mWindowAttributes.softInputMode &=
+                        ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
+                ((WindowManager.LayoutParams) mView.getLayoutParams())
+                        .softInputMode &=
+                        ~WindowManager.LayoutParams
+                                .SOFT_INPUT_IS_FORWARD_NAVIGATION;
+                mHasHadWindowFocus = true;
+            } else {
+                if (mPointerCapture) {
+                    handlePointerCaptureChanged(false);
+                }
+            }
+        }
+        mFirstInputStage.onWindowFocusChanged(hasWindowFocus);
+    }
+
     private void handleOutOfResourcesException(Surface.OutOfResourcesException e) {
         Log.e(mTag, "OutOfResourcesException initializing HW surface", e);
         try {
@@ -3909,81 +4003,7 @@
                     }
                     break;
                 case MSG_WINDOW_FOCUS_CHANGED: {
-                    final boolean hasWindowFocus = msg.arg1 != 0;
-                    if (mAdded) {
-                        mAttachInfo.mHasWindowFocus = hasWindowFocus;
-
-                        profileRendering(hasWindowFocus);
-
-                        if (hasWindowFocus) {
-                            boolean inTouchMode = msg.arg2 != 0;
-                            ensureTouchModeLocally(inTouchMode);
-                            if (mAttachInfo.mThreadedRenderer != null && mSurface.isValid()) {
-                                mFullRedrawNeeded = true;
-                                try {
-                                    final WindowManager.LayoutParams lp = mWindowAttributes;
-                                    final Rect surfaceInsets = lp != null ? lp.surfaceInsets : null;
-                                    mAttachInfo.mThreadedRenderer.initializeIfNeeded(
-                                            mWidth, mHeight, mAttachInfo, mSurface, surfaceInsets);
-                                } catch (OutOfResourcesException e) {
-                                    Log.e(mTag, "OutOfResourcesException locking surface", e);
-                                    try {
-                                        if (!mWindowSession.outOfMemory(mWindow)) {
-                                            Slog.w(mTag, "No processes killed for memory;"
-                                                    + " killing self");
-                                            Process.killProcess(Process.myPid());
-                                        }
-                                    } catch (RemoteException ex) {
-                                    }
-                                    // Retry in a bit.
-                                    sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2),
-                                            500);
-                                    return;
-                                }
-                            }
-                        }
-
-                        mLastWasImTarget = WindowManager.LayoutParams
-                                .mayUseInputMethod(mWindowAttributes.flags);
-
-                        InputMethodManager imm = InputMethodManager.peekInstance();
-                        if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
-                            imm.onPreWindowFocus(mView, hasWindowFocus);
-                        }
-                        if (mView != null) {
-                            mAttachInfo.mKeyDispatchState.reset();
-                            mView.dispatchWindowFocusChanged(hasWindowFocus);
-                            mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
-
-                            if (mAttachInfo.mTooltipHost != null) {
-                                mAttachInfo.mTooltipHost.hideTooltip();
-                            }
-                        }
-
-                        // Note: must be done after the focus change callbacks,
-                        // so all of the view state is set up correctly.
-                        if (hasWindowFocus) {
-                            if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
-                                imm.onPostWindowFocus(mView, mView.findFocus(),
-                                        mWindowAttributes.softInputMode,
-                                        !mHasHadWindowFocus, mWindowAttributes.flags);
-                            }
-                            // Clear the forward bit.  We can just do this directly, since
-                            // the window manager doesn't care about it.
-                            mWindowAttributes.softInputMode &=
-                                    ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
-                            ((WindowManager.LayoutParams) mView.getLayoutParams())
-                                    .softInputMode &=
-                                        ~WindowManager.LayoutParams
-                                                .SOFT_INPUT_IS_FORWARD_NAVIGATION;
-                            mHasHadWindowFocus = true;
-                        } else {
-                            if (mPointerCapture) {
-                                handlePointerCaptureChanged(false);
-                            }
-                        }
-                    }
-                    mFirstInputStage.onWindowFocusChanged(hasWindowFocus);
+                    handleWindowFocusChanged();
                 } break;
                 case MSG_DIE:
                     doDie();
@@ -6854,6 +6874,7 @@
         }
 
         if (stage != null) {
+            handleWindowFocusChanged();
             stage.deliver(q);
         } else {
             finishInputEvent(q);
@@ -7159,10 +7180,12 @@
     }
 
     public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
+        synchronized (this) {
+            mUpcomingWindowFocus = hasFocus;
+            mUpcomingInTouchMode = inTouchMode;
+        }
         Message msg = Message.obtain();
         msg.what = MSG_WINDOW_FOCUS_CHANGED;
-        msg.arg1 = hasFocus ? 1 : 0;
-        msg.arg2 = inTouchMode ? 1 : 0;
         mHandler.sendMessage(msg);
     }
 
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 6d84aa0..d0dbff0e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -24,7 +24,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemService;
-import android.annotation.TestApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -1010,20 +1009,14 @@
     }
 
     /**
-     * Gets the user data used for <a href="#FieldsClassification">fields classification</a>.
+     * Gets the user data used for
+     * <a href="AutofillService.html#FieldClassification">field classification</a>.
      *
      * <p><b>Note:</b> This method should only be called by an app providing an autofill service.
      *
-     * TODO(b/67867469):
-     *  - proper javadoc
-     *  - unhide / remove testApi
-     *
      * @return value previously set by {@link #setUserData(UserData)} or {@code null} if it was
      * reset or if the caller currently does not have an enabled autofill service for the user.
-     *
-     * @hide
      */
-    @TestApi
     @Nullable public UserData getUserData() {
         try {
             return mService.getUserData();
@@ -1034,21 +1027,13 @@
     }
 
     /**
-     * Sets the user data used for <a href="#FieldsClassification">fields classification</a>.
+     * Sets the user data used for
+     * <a href="AutofillService.html#FieldClassification">field classification</a>
      *
      * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
      * and it's ignored if the caller currently doesn't have an enabled autofill service for
      * the user.
-     *
-     * TODO(b/67867469):
-     *  - proper javadoc
-     *  - unhide / remove testApi
-     *  - add unit tests:
-     *    - call set / get / verify
-     *
-     * @hide
      */
-    @TestApi
     public void setUserData(@Nullable UserData userData) {
         try {
             mService.setUserData(userData);
@@ -1058,13 +1043,17 @@
     }
 
     /**
-     * TODO(b/67867469):
-     * - proper javadoc
-     * - mention this method in other places
-     * - unhide / remove testApi
-     * @hide
+     * Checks if <a href="AutofillService.html#FieldClassification">field classification</a> is
+     * enabled.
+     *
+     * <p>As field classification is an expensive operation, it could be disabled, either
+     * temporarily (for example, because the service exceeded a rate-limit threshold) or
+     * permanently (for example, because the device is a low-level device).
+     *
+     * <p><b>Note:</b> This method should only be called by an app providing an autofill service,
+     * and it's ignored if the caller currently doesn't have an enabled autofill service for
+     * the user.
      */
-    @TestApi
     public boolean isFieldClassificationEnabled() {
         try {
             return mService.isFieldClassificationEnabled();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index a050a3c..5d6d5bc 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -11228,7 +11228,9 @@
         reportChangesToStatsLog(mHaveBatteryLevel ? mHistoryCur : null,
                 status, plugType, level, temp);
 
-        final boolean onBattery = plugType == BATTERY_PLUGGED_NONE;
+        final boolean onBattery =
+            plugType == BATTERY_PLUGGED_NONE &&
+            status != BatteryManager.BATTERY_STATUS_UNKNOWN;
         final long uptime = mClocks.uptimeMillis();
         final long elapsedRealtime = mClocks.elapsedRealtime();
         if (!mHaveBatteryLevel) {
@@ -11262,7 +11264,8 @@
                 mRecordingHistory = true;
                 startRecordingHistory(elapsedRealtime, uptime, true);
             }
-        } else if (level < 96) {
+        } else if (level < 96 &&
+            status != BatteryManager.BATTERY_STATUS_UNKNOWN) {
             if (!mRecordingHistory) {
                 mRecordingHistory = true;
                 startRecordingHistory(elapsedRealtime, uptime, true);
@@ -11400,9 +11403,12 @@
                 addHistoryRecordLocked(elapsedRealtime, uptime);
             }
         }
-        if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) {
-            // We don't record history while we are plugged in and fully charged.
-            // The next time we are unplugged, history will be cleared.
+        if (!onBattery &&
+            (status == BatteryManager.BATTERY_STATUS_FULL ||
+             status == BatteryManager.BATTERY_STATUS_UNKNOWN)) {
+            // We don't record history while we are plugged in and fully charged
+            // (or when battery is not present).  The next time we are
+            // unplugged, history will be cleared.
             mRecordingHistory = DEBUG;
         }
 
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 4be6b28..e871003 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -27,7 +27,6 @@
 import android.content.Context;
 import android.content.pm.UserInfo;
 import android.os.AsyncTask;
-import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -35,7 +34,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.IStorageManager;
@@ -964,9 +962,12 @@
 
     /**
      * Retrieves whether the current profile and device locks can be unified.
+     * @param userHandle profile user handle.
      */
     public boolean isSeparateProfileChallengeAllowedToUnify(int userHandle) {
-        return getDevicePolicyManager().isProfileActivePasswordSufficientForParent(userHandle);
+        return getDevicePolicyManager().isProfileActivePasswordSufficientForParent(userHandle)
+                && !getUserManager().hasUserRestriction(
+                        UserManager.DISALLOW_UNIFIED_PASSWORD, UserHandle.of(userHandle));
     }
 
     private boolean hasSeparateChallenge(int userHandle) {
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index 17b98da..cc2646c 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -236,17 +236,15 @@
         return INSTALL_SUCCEEDED;
     }
 
-    char localTmpFileName[nativeLibPath.size() + TMP_FILE_PATTERN_LEN + 2];
+    char localTmpFileName[nativeLibPath.size() + TMP_FILE_PATTERN_LEN + 1];
     if (strlcpy(localTmpFileName, nativeLibPath.c_str(), sizeof(localTmpFileName))
             != nativeLibPath.size()) {
         ALOGD("Couldn't allocate local file name for library");
         return INSTALL_FAILED_INTERNAL_ERROR;
     }
 
-    *(localTmpFileName + nativeLibPath.size()) = '/';
-
     if (strlcpy(localTmpFileName + nativeLibPath.size(), TMP_FILE_PATTERN,
-                    TMP_FILE_PATTERN_LEN - nativeLibPath.size()) != TMP_FILE_PATTERN_LEN) {
+                    TMP_FILE_PATTERN_LEN + 1) != TMP_FILE_PATTERN_LEN) {
         ALOGI("Couldn't allocate temporary file name for library");
         return INSTALL_FAILED_INTERNAL_ERROR;
     }
diff --git a/core/proto/android/graphics/point.proto b/core/proto/android/graphics/point.proto
new file mode 100644
index 0000000..5ae17cb
--- /dev/null
+++ b/core/proto/android/graphics/point.proto
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package android.graphics;
+
+option java_multiple_files = true;
+
+message PointProto {
+  optional int32 x = 1;
+  optional int32 y = 2;
+}
+
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 915fe9d..ef7b6b0 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -170,6 +170,8 @@
   optional WindowStateAnimatorProto animator = 13;
   optional bool animating_exit = 14;
   repeated WindowStateProto child_windows = 15;
+  optional .android.graphics.RectProto surface_position = 16;
+  optional .android.graphics.RectProto shown_position = 17;
 }
 
 message IdentifierProto {
diff --git a/core/res/res/layout/activity_list_item_2.xml b/core/res/res/layout/activity_list_item_2.xml
index 608e986..af1963c 100644
--- a/core/res/res/layout/activity_list_item_2.xml
+++ b/core/res/res/layout/activity_list_item_2.xml
@@ -21,5 +21,6 @@
     android:textAppearance="?attr/textAppearanceListItemSmall"
     android:gravity="center_vertical"
     android:drawablePadding="14dip"
+    android:textAlignment="viewStart"
     android:paddingStart="?attr/listPreferredItemPaddingStart"
     android:paddingEnd="?attr/listPreferredItemPaddingEnd" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 8377751..dc791cf 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2097,6 +2097,10 @@
          movement threshold where scrolling should begin. -->
     <dimen name="config_viewConfigurationTouchSlop">8dp</dimen>
 
+    <!-- Base "hover slop" value used by ViewConfiguration as a
+         movement threshold under which hover is considered "stationary". -->
+    <dimen name="config_viewConfigurationHoverSlop">4dp</dimen>
+
     <!-- Minimum velocity to initiate a fling, as measured in dips per second. -->
     <dimen name="config_viewMinFlingVelocity">50dp</dimen>
 
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index be7ce5f..4b2424f 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -470,6 +470,7 @@
   <java-symbol type="dimen" name="config_minScrollbarTouchTarget" />
   <java-symbol type="dimen" name="config_prefDialogWidth" />
   <java-symbol type="dimen" name="config_viewConfigurationTouchSlop" />
+  <java-symbol type="dimen" name="config_viewConfigurationHoverSlop" />
   <java-symbol type="dimen" name="config_viewMinFlingVelocity" />
   <java-symbol type="dimen" name="config_viewMaxFlingVelocity" />
   <java-symbol type="dimen" name="config_scrollbarSize" />
diff --git a/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java
new file mode 100644
index 0000000..230655d
--- /dev/null
+++ b/core/tests/coretests/src/android/database/sqlite/SQLiteCompatibilityWalFlagsTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.database.sqlite;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.database.DatabaseUtils;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+/**
+ * Tests for {@link SQLiteCompatibilityWalFlags}
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SQLiteCompatibilityWalFlagsTest {
+
+    private SQLiteDatabase mDatabase;
+
+    @After
+    public void tearDown() {
+        SQLiteCompatibilityWalFlags.reset();
+        if (mDatabase != null) {
+            mDatabase.close();
+            SQLiteDatabase.deleteDatabase(new File(mDatabase.getPath()));
+        }
+    }
+
+    @Test
+    public void testParseConfig() {
+        SQLiteCompatibilityWalFlags.init("");
+        assertFalse(SQLiteCompatibilityWalFlags.areFlagsSet());
+
+        SQLiteCompatibilityWalFlags.init(null);
+        assertFalse(SQLiteCompatibilityWalFlags.areFlagsSet());
+
+        SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=false,wal_syncmode=OFF");
+        assertTrue(SQLiteCompatibilityWalFlags.areFlagsSet());
+        assertFalse(SQLiteCompatibilityWalFlags.isCompatibilityWalSupported());
+        assertEquals("OFF", SQLiteCompatibilityWalFlags.getWALSyncMode());
+
+        SQLiteCompatibilityWalFlags.init("wal_syncmode=VALUE");
+        assertTrue(SQLiteCompatibilityWalFlags.areFlagsSet());
+        assertEquals(SQLiteGlobal.isCompatibilityWalSupported(),
+                SQLiteCompatibilityWalFlags.isCompatibilityWalSupported());
+        assertEquals("VALUE", SQLiteCompatibilityWalFlags.getWALSyncMode());
+
+        SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=true");
+        assertTrue(SQLiteCompatibilityWalFlags.areFlagsSet());
+        assertEquals(SQLiteGlobal.getWALSyncMode(),
+                SQLiteCompatibilityWalFlags.getWALSyncMode());
+        assertTrue(SQLiteCompatibilityWalFlags.isCompatibilityWalSupported());
+
+        SQLiteCompatibilityWalFlags.reset();
+        SQLiteCompatibilityWalFlags.init("Invalid value");
+        assertFalse(SQLiteCompatibilityWalFlags.areFlagsSet());
+    }
+
+    @Test
+    public void testApplyFlags() {
+        Context ctx = InstrumentationRegistry.getContext();
+
+        SQLiteCompatibilityWalFlags.init("compatibility_wal_supported=true,wal_syncmode=NORMAL");
+        mDatabase = SQLiteDatabase
+                .openOrCreateDatabase(ctx.getDatabasePath("SQLiteCompatibilityWalFlagsTest"), null);
+        String journalMode = DatabaseUtils.stringForQuery(mDatabase, "PRAGMA journal_mode", null);
+        assertEquals("WAL", journalMode.toUpperCase());
+        String syncMode = DatabaseUtils.stringForQuery(mDatabase, "PRAGMA synchronous", null);
+        assertEquals("Normal mode (1) is expected", "1", syncMode);
+    }
+
+
+}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index b03f054..1002453 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -335,6 +335,7 @@
                     Settings.Global.SMS_SHORT_CODES_UPDATE_CONTENT_URL,
                     Settings.Global.SMS_SHORT_CODES_UPDATE_METADATA_URL,
                     Settings.Global.SPEED_LABEL_CACHE_EVICTION_AGE_MILLIS,
+                    Settings.Global.SQLITE_COMPATIBILITY_WAL_FLAGS,
                     Settings.Global.STORAGE_BENCHMARK_INTERVAL,
                     Settings.Global.STORAGE_SETTINGS_CLOBBER_THRESHOLD,
                     Settings.Global.SYNC_MAX_RETRY_DELAY_IN_SECONDS,
@@ -420,13 +421,6 @@
     private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
              newHashSet(
                  Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
-                 // TODO(b/67867469): Move autofill settings below to
-                 // BACKUP_BLACKLISTED_SYSTEM_SETTINGS once feature is moved out of experimental
-                 Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
-                 Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
-                 Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE,
-                 Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH,
-                 Settings.Secure.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH,
                  Settings.Secure.ALLOWED_GEOLOCATION_ORIGINS,
                  Settings.Secure.ALWAYS_ON_VPN_APP,
                  Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN,
@@ -436,6 +430,11 @@
                  Settings.Secure.ASSIST_DISCLOSURE_ENABLED,
                  Settings.Secure.ASSIST_SCREENSHOT_ENABLED,
                  Settings.Secure.ASSIST_STRUCTURE_ENABLED,
+                 Settings.Secure.AUTOFILL_FEATURE_FIELD_CLASSIFICATION,
+                 Settings.Secure.AUTOFILL_USER_DATA_MAX_FIELD_CLASSIFICATION_IDS_SIZE,
+                 Settings.Secure.AUTOFILL_USER_DATA_MAX_USER_DATA_SIZE,
+                 Settings.Secure.AUTOFILL_USER_DATA_MAX_VALUE_LENGTH,
+                 Settings.Secure.AUTOFILL_USER_DATA_MIN_VALUE_LENGTH,
                  Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI,
                  Settings.Secure.AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED,
                  Settings.Secure.AUTOMATIC_STORAGE_MANAGER_ENABLED,
diff --git a/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.mk b/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.mk
index 193e601..b255c7e 100644
--- a/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.mk
+++ b/core/tests/hosttests/test-apps/ExternalSharedPerms/Android.mk
@@ -22,6 +22,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := junit
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 LOCAL_PACKAGE_NAME := ExternalSharedPermsTestApp
diff --git a/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.mk b/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.mk
index 9f44767..428b1f8 100644
--- a/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.mk
+++ b/core/tests/hosttests/test-apps/ExternalSharedPermsBT/Android.mk
@@ -22,6 +22,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := junit
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 LOCAL_PACKAGE_NAME := ExternalSharedPermsBTTestApp
diff --git a/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.mk b/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.mk
index b4d354c..93ece86 100644
--- a/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.mk
+++ b/core/tests/hosttests/test-apps/ExternalSharedPermsDiffKey/Android.mk
@@ -22,6 +22,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := junit
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 LOCAL_PACKAGE_NAME := ExternalSharedPermsDiffKeyTestApp
diff --git a/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.mk b/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.mk
index 969e588..b348966 100644
--- a/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.mk
+++ b/core/tests/hosttests/test-apps/ExternalSharedPermsFL/Android.mk
@@ -22,6 +22,8 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := junit
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 LOCAL_PACKAGE_NAME := ExternalSharedPermsFLTestApp
diff --git a/graphics/java/android/graphics/Point.java b/graphics/java/android/graphics/Point.java
index abcccbd..c6b6c66 100644
--- a/graphics/java/android/graphics/Point.java
+++ b/graphics/java/android/graphics/Point.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.proto.ProtoOutputStream;
 
 import java.io.PrintWriter;
 
@@ -121,6 +122,21 @@
         out.writeInt(y);
     }
 
+    /**
+     * Write to a protocol buffer output stream.
+     * Protocol buffer message definition at {@link android.graphics.PointProto}
+     *
+     * @param protoOutputStream Stream to write the Rect object to.
+     * @param fieldId           Field Id of the Rect as defined in the parent message
+     * @hide
+     */
+    public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) {
+        final long token = protoOutputStream.start(fieldId);
+        protoOutputStream.write(PointProto.X, x);
+        protoOutputStream.write(PointProto.Y, y);
+        protoOutputStream.end(token);
+    }
+
     public static final Parcelable.Creator<Point> CREATOR = new Parcelable.Creator<Point>() {
         /**
          * Return a new point from the data in the specified parcel.
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index 6830a74..57db20b 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -60,10 +60,12 @@
     /** Key prefix for user certificates. */
     public static final String USER_CERTIFICATE = "USRCERT_";
 
-    /** Key prefix for user private keys. */
+    /** Key prefix for user private and secret keys. */
     public static final String USER_PRIVATE_KEY = "USRPKEY_";
 
-    /** Key prefix for user secret keys. */
+    /** Key prefix for user secret keys.
+     *  @deprecated use {@code USER_PRIVATE_KEY} for this category instead.
+     */
     public static final String USER_SECRET_KEY = "USRSKEY_";
 
     /** Key prefix for VPN. */
@@ -235,8 +237,7 @@
          * Make sure every type is deleted. There can be all three types, so
          * don't use a conditional here.
          */
-        return deletePrivateKeyTypeForAlias(keystore, alias, uid)
-                & deleteSecretKeyTypeForAlias(keystore, alias, uid)
+        return deleteUserKeyTypeForAlias(keystore, alias, uid)
                 & deleteCertificateTypesForAlias(keystore, alias, uid);
     }
 
@@ -264,34 +265,27 @@
     }
 
     /**
-     * Delete private key for a particular {@code alias}.
+     * Delete user key for a particular {@code alias}.
      * Returns {@code true} if the entry no longer exists.
      */
-    static boolean deletePrivateKeyTypeForAlias(KeyStore keystore, String alias) {
-        return deletePrivateKeyTypeForAlias(keystore, alias, KeyStore.UID_SELF);
+    public static boolean deleteUserKeyTypeForAlias(KeyStore keystore, String alias) {
+        return deleteUserKeyTypeForAlias(keystore, alias, KeyStore.UID_SELF);
     }
 
     /**
-     * Delete private key for a particular {@code alias}.
+     * Delete user key for a particular {@code alias}.
      * Returns {@code true} if the entry no longer exists.
      */
-    static boolean deletePrivateKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
-        return keystore.delete(Credentials.USER_PRIVATE_KEY + alias, uid);
+    public static boolean deleteUserKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
+        return keystore.delete(Credentials.USER_PRIVATE_KEY + alias, uid) ||
+                keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
     }
 
     /**
-     * Delete secret key for a particular {@code alias}.
+     * Delete legacy prefixed entry for a particular {@code alias}
      * Returns {@code true} if the entry no longer exists.
      */
-    public static boolean deleteSecretKeyTypeForAlias(KeyStore keystore, String alias) {
-        return deleteSecretKeyTypeForAlias(keystore, alias, KeyStore.UID_SELF);
-    }
-
-    /**
-     * Delete secret key for a particular {@code alias}.
-     * Returns {@code true} if the entry no longer exists.
-     */
-    public static boolean deleteSecretKeyTypeForAlias(KeyStore keystore, String alias, int uid) {
+    public static boolean deleteLegacyKeyForAlias(KeyStore keystore, String alias, int uid) {
         return keystore.delete(Credentials.USER_SECRET_KEY + alias, uid);
     }
 }
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index b4331b2..eca52cc 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -16,6 +16,7 @@
 package android.security;
 
 import android.content.pm.StringParceledListSlice;
+import android.security.keymaster.KeymasterCertificateChain;
 import android.security.keystore.ParcelableKeyGenParameterSpec;
 
 /**
@@ -33,6 +34,7 @@
     void setUserSelectable(String alias, boolean isUserSelectable);
 
     boolean generateKeyPair(in String algorithm, in ParcelableKeyGenParameterSpec spec);
+    boolean attestKey(in String alias, in byte[] challenge, out KeymasterCertificateChain chain);
 
     // APIs used by CertInstaller and DevicePolicyManager
     String installCaCertificate(in byte[] caCertificate);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index 988e32c..f1d1e16 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -305,7 +305,7 @@
                 KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
                         mRng, (mKeySizeBits + 7) / 8);
         int flags = 0;
-        String keyAliasInKeystore = Credentials.USER_SECRET_KEY + spec.getKeystoreAlias();
+        String keyAliasInKeystore = Credentials.USER_PRIVATE_KEY + spec.getKeystoreAlias();
         KeyCharacteristics resultingKeyCharacteristics = new KeyCharacteristics();
         boolean success = false;
         try {
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
index f36c00c..55e6519 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreProvider.java
@@ -196,7 +196,7 @@
     }
 
     @NonNull
-    public static AndroidKeyStorePrivateKey getAndroidKeyStorePrivateKey(
+    private static AndroidKeyStorePrivateKey getAndroidKeyStorePrivateKey(
             @NonNull AndroidKeyStorePublicKey publicKey) {
         String keyAlgorithm = publicKey.getAlgorithm();
         if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
@@ -212,17 +212,25 @@
     }
 
     @NonNull
-    public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
-            @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
+    private static KeyCharacteristics getKeyCharacteristics(@NonNull KeyStore keyStore,
+            @NonNull String alias, int uid)
             throws UnrecoverableKeyException {
         KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
         int errorCode = keyStore.getKeyCharacteristics(
-                privateKeyAlias, null, null, uid, keyCharacteristics);
+                alias, null, null, uid, keyCharacteristics);
         if (errorCode != KeyStore.NO_ERROR) {
             throw (UnrecoverableKeyException)
-                    new UnrecoverableKeyException("Failed to obtain information about private key")
-                    .initCause(KeyStore.getKeyStoreException(errorCode));
+                    new UnrecoverableKeyException("Failed to obtain information about key")
+                            .initCause(KeyStore.getKeyStoreException(errorCode));
         }
+        return keyCharacteristics;
+    }
+
+    @NonNull
+    private static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
+            @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid,
+            KeyCharacteristics keyCharacteristics)
+            throws UnrecoverableKeyException {
         ExportResult exportResult = keyStore.exportKey(
                 privateKeyAlias, KeymasterDefs.KM_KEY_FORMAT_X509, null, null, uid);
         if (exportResult.resultCode != KeyStore.NO_ERROR) {
@@ -252,37 +260,56 @@
     }
 
     @NonNull
-    public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
+    public static AndroidKeyStorePublicKey loadAndroidKeyStorePublicKeyFromKeystore(
             @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
             throws UnrecoverableKeyException {
+        return loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid,
+                getKeyCharacteristics(keyStore, privateKeyAlias, uid));
+    }
+
+    @NonNull
+    private static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
+            @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid,
+            @NonNull KeyCharacteristics keyCharacteristics)
+            throws UnrecoverableKeyException {
         AndroidKeyStorePublicKey publicKey =
-                loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid);
+                loadAndroidKeyStorePublicKeyFromKeystore(keyStore, privateKeyAlias, uid,
+                        keyCharacteristics);
         AndroidKeyStorePrivateKey privateKey =
                 AndroidKeyStoreProvider.getAndroidKeyStorePrivateKey(publicKey);
         return new KeyPair(publicKey, privateKey);
     }
 
     @NonNull
-    public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
+    public static KeyPair loadAndroidKeyStoreKeyPairFromKeystore(
             @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
             throws UnrecoverableKeyException {
-        KeyPair keyPair = loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid);
+        return loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid,
+                getKeyCharacteristics(keyStore, privateKeyAlias, uid));
+    }
+
+    @NonNull
+    private static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
+            @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid,
+            @NonNull KeyCharacteristics keyCharacteristics)
+            throws UnrecoverableKeyException {
+        KeyPair keyPair = loadAndroidKeyStoreKeyPairFromKeystore(keyStore, privateKeyAlias, uid,
+                keyCharacteristics);
         return (AndroidKeyStorePrivateKey) keyPair.getPrivate();
     }
 
     @NonNull
-    public static AndroidKeyStoreSecretKey loadAndroidKeyStoreSecretKeyFromKeystore(
-            @NonNull KeyStore keyStore, @NonNull String secretKeyAlias, int uid)
+    public static AndroidKeyStorePrivateKey loadAndroidKeyStorePrivateKeyFromKeystore(
+            @NonNull KeyStore keyStore, @NonNull String privateKeyAlias, int uid)
             throws UnrecoverableKeyException {
-        KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
-        int errorCode = keyStore.getKeyCharacteristics(
-                secretKeyAlias, null, null, uid, keyCharacteristics);
-        if (errorCode != KeyStore.NO_ERROR) {
-            throw (UnrecoverableKeyException)
-                    new UnrecoverableKeyException("Failed to obtain information about key")
-                            .initCause(KeyStore.getKeyStoreException(errorCode));
-        }
+        return loadAndroidKeyStorePrivateKeyFromKeystore(keyStore, privateKeyAlias, uid,
+                getKeyCharacteristics(keyStore, privateKeyAlias, uid));
+    }
 
+    @NonNull
+    private static AndroidKeyStoreSecretKey loadAndroidKeyStoreSecretKeyFromKeystore(
+            @NonNull String secretKeyAlias, int uid, @NonNull KeyCharacteristics keyCharacteristics)
+            throws UnrecoverableKeyException {
         Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM);
         if (keymasterAlgorithm == null) {
             throw new UnrecoverableKeyException("Key algorithm unknown");
@@ -310,6 +337,29 @@
         return new AndroidKeyStoreSecretKey(secretKeyAlias, uid, keyAlgorithmString);
     }
 
+    public static AndroidKeyStoreKey loadAndroidKeyStoreKeyFromKeystore(
+            @NonNull KeyStore keyStore, @NonNull String userKeyAlias, int uid)
+            throws UnrecoverableKeyException  {
+        KeyCharacteristics keyCharacteristics = getKeyCharacteristics(keyStore, userKeyAlias, uid);
+
+        Integer keymasterAlgorithm = keyCharacteristics.getEnum(KeymasterDefs.KM_TAG_ALGORITHM);
+        if (keymasterAlgorithm == null) {
+            throw new UnrecoverableKeyException("Key algorithm unknown");
+        }
+
+        if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC ||
+                keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_AES) {
+            return loadAndroidKeyStoreSecretKeyFromKeystore(userKeyAlias, uid,
+                    keyCharacteristics);
+        } else if (keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_RSA ||
+                keymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) {
+            return loadAndroidKeyStorePrivateKeyFromKeystore(keyStore, userKeyAlias, uid,
+                    keyCharacteristics);
+        } else {
+            throw new UnrecoverableKeyException("Key algorithm unknown");
+        }
+    }
+
     /**
      * Returns an {@code AndroidKeyStore} {@link java.security.KeyStore}} of the specified UID.
      * The {@code KeyStore} contains keys and certificates owned by that UID. Such cross-UID
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
index 0379863..fdb885db 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKeyFactorySpi.java
@@ -64,7 +64,10 @@
         AndroidKeyStoreKey keystoreKey = (AndroidKeyStoreKey) key;
         String keyAliasInKeystore = keystoreKey.getAlias();
         String entryAlias;
-        if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
+        if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) {
+            entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length());
+        } else if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)){
+            // key has legacy prefix
             entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
         } else {
             throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
index bab4010..d73a9e2 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSpi.java
@@ -89,18 +89,14 @@
     @Override
     public Key engineGetKey(String alias, char[] password) throws NoSuchAlgorithmException,
             UnrecoverableKeyException {
-        if (isPrivateKeyEntry(alias)) {
-            String privateKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
-            return AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
-                    mKeyStore, privateKeyAlias, mUid);
-        } else if (isSecretKeyEntry(alias)) {
-            String secretKeyAlias = Credentials.USER_SECRET_KEY + alias;
-            return AndroidKeyStoreProvider.loadAndroidKeyStoreSecretKeyFromKeystore(
-                    mKeyStore, secretKeyAlias, mUid);
-        } else {
-            // Key not found
-            return null;
+        String userKeyAlias = Credentials.USER_PRIVATE_KEY + alias;
+        if (!mKeyStore.contains(userKeyAlias, mUid)) {
+            // try legacy prefix for backward compatibility
+            userKeyAlias = Credentials.USER_SECRET_KEY + alias;
+            if (!mKeyStore.contains(userKeyAlias, mUid)) return null;
         }
+        return AndroidKeyStoreProvider.loadAndroidKeyStoreKeyFromKeystore(mKeyStore, userKeyAlias,
+                mUid);
     }
 
     @Override
@@ -540,7 +536,7 @@
             } else {
                 // Keep the stored private key around -- delete all other entry types
                 Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
-                Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid);
+                Credentials.deleteLegacyKeyForAlias(mKeyStore, alias, mUid);
             }
 
             // Store the leaf certificate
@@ -565,7 +561,7 @@
                     Credentials.deleteAllTypesForAlias(mKeyStore, alias, mUid);
                 } else {
                     Credentials.deleteCertificateTypesForAlias(mKeyStore, alias, mUid);
-                    Credentials.deleteSecretKeyTypeForAlias(mKeyStore, alias, mUid);
+                    Credentials.deleteLegacyKeyForAlias(mKeyStore, alias, mUid);
                 }
             }
         }
@@ -588,12 +584,17 @@
             if (keyAliasInKeystore == null) {
                 throw new KeyStoreException("KeyStore-backed secret key does not have an alias");
             }
-            if (!keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)) {
-                throw new KeyStoreException("KeyStore-backed secret key has invalid alias: "
-                        + keyAliasInKeystore);
+            String keyAliasPrefix = Credentials.USER_PRIVATE_KEY;
+            if (!keyAliasInKeystore.startsWith(keyAliasPrefix)) {
+                // try legacy prefix
+                keyAliasPrefix = Credentials.USER_SECRET_KEY;
+                if (!keyAliasInKeystore.startsWith(keyAliasPrefix)) {
+                    throw new KeyStoreException("KeyStore-backed secret key has invalid alias: "
+                            + keyAliasInKeystore);
+                }
             }
             String keyEntryAlias =
-                    keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
+                    keyAliasInKeystore.substring(keyAliasPrefix.length());
             if (!entryAlias.equals(keyEntryAlias)) {
                 throw new KeyStoreException("Can only replace KeyStore-backed keys with same"
                         + " alias: " + entryAlias + " != " + keyEntryAlias);
@@ -728,7 +729,7 @@
         }
 
         Credentials.deleteAllTypesForAlias(mKeyStore, entryAlias, mUid);
-        String keyAliasInKeystore = Credentials.USER_SECRET_KEY + entryAlias;
+        String keyAliasInKeystore = Credentials.USER_PRIVATE_KEY + entryAlias;
         int errorCode = mKeyStore.importKey(
                 keyAliasInKeystore,
                 args,
@@ -827,24 +828,10 @@
     }
 
     private boolean isKeyEntry(String alias) {
-        return isPrivateKeyEntry(alias) || isSecretKeyEntry(alias);
+        return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid) ||
+                mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid);
     }
 
-    private boolean isPrivateKeyEntry(String alias) {
-        if (alias == null) {
-            throw new NullPointerException("alias == null");
-        }
-
-        return mKeyStore.contains(Credentials.USER_PRIVATE_KEY + alias, mUid);
-    }
-
-    private boolean isSecretKeyEntry(String alias) {
-        if (alias == null) {
-            throw new NullPointerException("alias == null");
-        }
-
-        return mKeyStore.contains(Credentials.USER_SECRET_KEY + alias, mUid);
-    }
 
     private boolean isCertificateEntry(String alias) {
         if (alias == null) {
diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java
index cf4347d..0811100 100644
--- a/keystore/java/android/security/keystore/AttestationUtils.java
+++ b/keystore/java/android/security/keystore/AttestationUtils.java
@@ -73,6 +73,33 @@
     public static final int ID_TYPE_MEID = 3;
 
     /**
+     * Creates an array of X509Certificates from the provided KeymasterCertificateChain.
+     *
+     * @hide Only called by the DevicePolicyManager.
+     */
+    @NonNull public static X509Certificate[] parseCertificateChain(
+            final KeymasterCertificateChain kmChain) throws
+            KeyAttestationException {
+        // Extract certificate chain.
+        final Collection<byte[]> rawChain = kmChain.getCertificates();
+        if (rawChain.size() < 2) {
+            throw new KeyAttestationException("Attestation certificate chain contained "
+                    + rawChain.size() + " entries. At least two are required.");
+        }
+        final ByteArrayOutputStream concatenatedRawChain = new ByteArrayOutputStream();
+        try {
+            for (final byte[] cert : rawChain) {
+                concatenatedRawChain.write(cert);
+            }
+            return CertificateFactory.getInstance("X.509").generateCertificates(
+                    new ByteArrayInputStream(concatenatedRawChain.toByteArray()))
+                            .toArray(new X509Certificate[0]);
+        } catch (Exception e) {
+            throw new KeyAttestationException("Unable to construct certificate chain", e);
+        }
+    }
+
+    /**
      * Performs attestation of the device's identifiers. This method returns a certificate chain
      * whose first element contains the requested device identifiers in an extension. The device's
      * manufacturer, model, brand, device and product are always also included in the attestation.
@@ -173,22 +200,18 @@
                     KeyStore.getKeyStoreException(errorCode));
         }
 
-        // Extract certificate chain.
-        final Collection<byte[]> rawChain = outChain.getCertificates();
-        if (rawChain.size() < 2) {
-            throw new DeviceIdAttestationException("Attestation certificate chain contained "
-                    + rawChain.size() + " entries. At least two are required.");
-        }
-        final ByteArrayOutputStream concatenatedRawChain = new ByteArrayOutputStream();
         try {
-            for (final byte[] cert : rawChain) {
-                concatenatedRawChain.write(cert);
-            }
-            return CertificateFactory.getInstance("X.509").generateCertificates(
-                    new ByteArrayInputStream(concatenatedRawChain.toByteArray()))
-                            .toArray(new X509Certificate[0]);
-        } catch (Exception e) {
-            throw new DeviceIdAttestationException("Unable to construct certificate chain", e);
+            return parseCertificateChain(outChain);
+        } catch (KeyAttestationException e) {
+            throw new DeviceIdAttestationException(e.getMessage(), e);
         }
     }
+
+    /**
+     * Returns true if the attestation chain provided is a valid key attestation chain.
+     * @hide
+     */
+    public static boolean isChainValid(KeymasterCertificateChain chain) {
+        return chain != null && chain.getCertificates().size() >= 2;
+    }
 }
diff --git a/keystore/java/android/security/keystore/KeyAttestationException.java b/keystore/java/android/security/keystore/KeyAttestationException.java
new file mode 100644
index 0000000..6cf5fb2
--- /dev/null
+++ b/keystore/java/android/security/keystore/KeyAttestationException.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+/**
+ * Thrown when {@link AttestationUtils} is unable to attest the given key or handle
+ * the resulting attestation record.
+ *
+ * @hide
+ */
+public class KeyAttestationException extends Exception {
+    /**
+     * Constructs a new {@code KeyAttestationException} with the current stack trace and the
+     * specified detail message.
+     *
+     * @param detailMessage the detail message for this exception.
+     */
+    public KeyAttestationException(String detailMessage) {
+        super(detailMessage);
+    }
+
+    /**
+     * Constructs a new {@code KeyAttestationException} with the current stack trace, the
+     * specified detail message and the specified cause.
+     *
+     * @param message the detail message for this exception.
+     * @param cause the cause of this exception, may be {@code null}.
+     */
+    public KeyAttestationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 87677d4..1238d87 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -680,6 +680,40 @@
         }
 
         /**
+         * A Builder constructor taking in an already-built KeyGenParameterSpec, useful for
+         * changing values of the KeyGenParameterSpec quickly.
+         * @hide Should be used internally only.
+         */
+        public Builder(@NonNull KeyGenParameterSpec sourceSpec) {
+            this(sourceSpec.getKeystoreAlias(), sourceSpec.getPurposes());
+            mUid = sourceSpec.getUid();
+            mKeySize = sourceSpec.getKeySize();
+            mSpec = sourceSpec.getAlgorithmParameterSpec();
+            mCertificateSubject = sourceSpec.getCertificateSubject();
+            mCertificateSerialNumber = sourceSpec.getCertificateSerialNumber();
+            mCertificateNotBefore = sourceSpec.getCertificateNotBefore();
+            mCertificateNotAfter = sourceSpec.getCertificateNotAfter();
+            mKeyValidityStart = sourceSpec.getKeyValidityStart();
+            mKeyValidityForOriginationEnd = sourceSpec.getKeyValidityForOriginationEnd();
+            mKeyValidityForConsumptionEnd = sourceSpec.getKeyValidityForConsumptionEnd();
+            mPurposes = sourceSpec.getPurposes();
+            if (sourceSpec.isDigestsSpecified()) {
+                mDigests = sourceSpec.getDigests();
+            }
+            mEncryptionPaddings = sourceSpec.getEncryptionPaddings();
+            mSignaturePaddings = sourceSpec.getSignaturePaddings();
+            mBlockModes = sourceSpec.getBlockModes();
+            mRandomizedEncryptionRequired = sourceSpec.isRandomizedEncryptionRequired();
+            mUserAuthenticationRequired = sourceSpec.isUserAuthenticationRequired();
+            mUserAuthenticationValidityDurationSeconds =
+                sourceSpec.getUserAuthenticationValidityDurationSeconds();
+            mAttestationChallenge = sourceSpec.getAttestationChallenge();
+            mUniqueIdIncluded = sourceSpec.isUniqueIdIncluded();
+            mUserAuthenticationValidWhileOnBody = sourceSpec.isUserAuthenticationValidWhileOnBody();
+            mInvalidatedByBiometricEnrollment = sourceSpec.isInvalidatedByBiometricEnrollment();
+        }
+
+        /**
          * Sets the UID which will own the key.
          *
          * @param uid UID or {@code -1} for the UID of the current process.
diff --git a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
index 73b489f..254b6be 100644
--- a/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
+++ b/keystore/tests/src/android/security/ParcelableKeyGenParameterSpecTest.java
@@ -55,7 +55,7 @@
     static final int USER_AUTHENTICATION_DURATION = 300;
     static final byte[] ATTESTATION_CHALLENGE = new byte[] {'c', 'h'};
 
-    KeyGenParameterSpec configureDefaultSpec() {
+    public static KeyGenParameterSpec configureDefaultSpec() {
         return new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES)
                 .setUid(UID)
                 .setKeySize(KEYSIZE)
@@ -80,7 +80,7 @@
                 .build();
     }
 
-    void validateSpecValues(KeyGenParameterSpec spec, int uid, String alias) {
+    public static void validateSpecValues(KeyGenParameterSpec spec, int uid, String alias) {
         assertThat(spec.getKeystoreAlias(), is(alias));
         assertThat(spec.getPurposes(), is(KEY_PURPOSES));
         assertThat(spec.getUid(), is(uid));
diff --git a/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
new file mode 100644
index 0000000..865cad4
--- /dev/null
+++ b/keystore/tests/src/android/security/keystore/KeyGenParameterSpecTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.security.ParcelableKeyGenParameterSpecTest;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Unit tests for {@link KeyGenParameterSpec}. */
+@RunWith(AndroidJUnit4.class)
+public final class KeyGenParameterSpecTest {
+    static final String ALIAS = "keystore-alias";
+    static final int KEY_PURPOSES = KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY;
+
+    @Test
+    public void testBuilderCopyingValues() {
+        KeyGenParameterSpec spec = ParcelableKeyGenParameterSpecTest.configureDefaultSpec();
+        KeyGenParameterSpec copiedSpec =
+                new KeyGenParameterSpec.Builder(spec).build();
+        ParcelableKeyGenParameterSpecTest.validateSpecValues(
+                copiedSpec, spec.getUid(), spec.getKeystoreAlias());
+    }
+
+    @Test
+    public void testBuilderCopyingEmptyValues() {
+        KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(ALIAS, KEY_PURPOSES).build();
+        KeyGenParameterSpec copiedSpec = new KeyGenParameterSpec.Builder(spec).build();
+
+        assertThat(copiedSpec.getKeystoreAlias(), is(ALIAS));
+        assertThat(copiedSpec.getPurposes(), is(KEY_PURPOSES));
+    }
+
+    @Test
+    public void testCanModifyValuesInCopiedBuilder() {
+        KeyGenParameterSpec spec = ParcelableKeyGenParameterSpecTest.configureDefaultSpec();
+        KeyGenParameterSpec copiedSpec =
+                new KeyGenParameterSpec.Builder(spec)
+                .setAttestationChallenge(null)
+                .build();
+
+        assertEquals(copiedSpec.getAttestationChallenge(), null);
+    }
+}
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index afdd339..f41956c 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -27,9 +27,11 @@
 
 #include <cutils/ashmem.h>
 #include <log/log.h>
+#include <sstream>
 
 #include "Properties.h"
 #include "utils/TimeUtils.h"
+#include "utils/Trace.h"
 
 namespace android {
 namespace uirenderer {
@@ -150,6 +152,19 @@
             (*mGlobalData)->reportJankType((JankType)i);
         }
     }
+
+    // Log daveys since they are weird and we don't know what they are (b/70339576)
+    if (totalDuration >= 700_ms) {
+        static int sDaveyCount = 0;
+        std::stringstream ss;
+        ss << "Davey! duration=" << ns2ms(totalDuration) << "ms; ";
+        for (size_t i = 0; i < static_cast<size_t>(FrameInfoIndex::NumIndexes); i++) {
+            ss << FrameInfoNames[i] << "=" << frame[i] << ", ";
+        }
+        ALOGI("%s", ss.str().c_str());
+        // Just so we have something that counts up, the value is largely irrelevant
+        ATRACE_INT(ss.str().c_str(), ++sDaveyCount);
+    }
 }
 
 void JankTracker::dumpData(int fd, const ProfileDataDescription* description,
diff --git a/nfc-extras/tests/Android.mk b/nfc-extras/tests/Android.mk
index be2bb53..34d6508 100644
--- a/nfc-extras/tests/Android.mk
+++ b/nfc-extras/tests/Android.mk
@@ -20,7 +20,8 @@
 
 LOCAL_JAVA_LIBRARIES := \
     android.test.runner \
-    com.android.nfc_extras
+    com.android.nfc_extras \
+    android.test.base.stubs
 
 LOCAL_STATIC_JAVA_LIBRARIES := junit
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index 27bc599..9f39321 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -49,6 +49,7 @@
     private boolean mIsVerifyUnlockOnly;
     private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
     private SecurityCallback mSecurityCallback;
+    private AlertDialog mAlertDialog;
 
     private final KeyguardUpdateMonitor mUpdateMonitor;
 
@@ -95,6 +96,10 @@
 
     @Override
     public void onPause() {
+        if (mAlertDialog != null) {
+            mAlertDialog.dismiss();
+            mAlertDialog = null;
+        }
         if (mCurrentSecuritySelection != SecurityMode.None) {
             getSecurityView(mCurrentSecuritySelection).onPause();
         }
@@ -174,16 +179,20 @@
     }
 
     private void showDialog(String title, String message) {
-        final AlertDialog dialog = new AlertDialog.Builder(mContext)
+        if (mAlertDialog != null) {
+            mAlertDialog.dismiss();
+        }
+
+        mAlertDialog = new AlertDialog.Builder(mContext)
             .setTitle(title)
             .setMessage(message)
             .setCancelable(false)
             .setNeutralButton(R.string.ok, null)
             .create();
         if (!(mContext instanceof Activity)) {
-            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+            mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
         }
-        dialog.show();
+        mAlertDialog.show();
     }
 
     private void showTimeoutDialog(int userId, int timeoutMs) {
diff --git a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
index b194de4..eff84c6 100644
--- a/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/ViewMediatorCallback.java
@@ -76,6 +76,12 @@
     void playTrustedSound();
 
     /**
+     * When the bouncer is shown or hides
+     * @param shown
+     */
+    void onBouncerVisiblityChanged(boolean shown);
+
+    /**
      * @return true if the screen is on
      */
     boolean isScreenOn();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 8fa66e0f..c4d9cf5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -613,6 +613,13 @@
         }
 
         @Override
+        public void onBouncerVisiblityChanged(boolean shown) {
+            synchronized (KeyguardViewMediator.this) {
+                adjustStatusBarLocked(shown);
+            }
+        }
+
+        @Override
         public void playTrustedSound() {
             KeyguardViewMediator.this.playTrustedSound();
         }
@@ -1862,6 +1869,10 @@
     }
 
     private void adjustStatusBarLocked() {
+        adjustStatusBarLocked(false /* forceHideHomeRecentsButtons */);
+    }
+
+    private void adjustStatusBarLocked(boolean forceHideHomeRecentsButtons) {
         if (mStatusBarManager == null) {
             mStatusBarManager = (StatusBarManager)
                     mContext.getSystemService(Context.STATUS_BAR_SERVICE);
@@ -1872,19 +1883,14 @@
             // Disable aspects of the system/status/navigation bars that must not be re-enabled by
             // windows that appear on top, ever
             int flags = StatusBarManager.DISABLE_NONE;
-            if (mShowing) {
-                // Permanently disable components not available when keyguard is enabled
-                // (like recents). Temporary enable/disable (e.g. the "back" button) are
-                // done in KeyguardHostView.
-                flags |= StatusBarManager.DISABLE_RECENT;
-            }
-            if (isShowingAndNotOccluded()) {
-                flags |= StatusBarManager.DISABLE_HOME;
+            if (forceHideHomeRecentsButtons || isShowingAndNotOccluded()) {
+                flags |= StatusBarManager.DISABLE_HOME | StatusBarManager.DISABLE_RECENT;
             }
 
             if (DEBUG) {
                 Log.d(TAG, "adjustStatusBarLocked: mShowing=" + mShowing + " mOccluded=" + mOccluded
-                        + " isSecure=" + isSecure() + " --> flags=0x" + Integer.toHexString(flags));
+                        + " isSecure=" + isSecure() + " force=" + forceHideHomeRecentsButtons
+                        +  " --> flags=0x" + Integer.toHexString(flags));
             }
 
             mStatusBarManager.disable(flags);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 663f206..8359690 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -24,6 +24,8 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
+import android.app.KeyguardManager;
+import android.app.trust.TrustManager;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
@@ -32,6 +34,7 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.drawable.Drawable;
+import android.os.AsyncTask.Status;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.util.ArraySet;
@@ -225,6 +228,7 @@
         }
     };
 
+    private TrustManager mTrustManager;
     protected Context mContext;
     protected Handler mHandler;
     TaskStackListenerImpl mTaskStackListener;
@@ -271,6 +275,8 @@
         // Initialize the static configuration resources
         mDummyStackView = new TaskStackView(mContext);
         reloadResources();
+
+        mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
     }
 
     public void onBootCompleted() {
@@ -309,8 +315,7 @@
      * {@link Recents#onBusEvent(ScreenPinningRequestEvent)}.
      */
     public void onStartScreenPinning(Context context, int taskId) {
-        SystemUIApplication app = (SystemUIApplication) context;
-        StatusBar statusBar = app.getComponent(StatusBar.class);
+        final StatusBar statusBar = getStatusBar();
         if (statusBar != null) {
             statusBar.showScreenPinningRequest(taskId, false);
         }
@@ -350,8 +355,8 @@
             if (forceVisible || !ssp.isRecentsActivityVisible(isHomeStackVisible)) {
                 ActivityManager.RunningTaskInfo runningTask =
                         ActivityManagerWrapper.getInstance().getRunningTask();
-                startRecentsActivity(runningTask, isHomeStackVisible.value || fromHome, animate,
-                        growTarget);
+                startRecentsActivityAndDismissKeyguardIfNeeded(runningTask,
+                        isHomeStackVisible.value || fromHome, animate, growTarget);
             }
         } catch (ActivityNotFoundException e) {
             Log.e(TAG, "Failed to launch RecentsActivity", e);
@@ -442,8 +447,8 @@
                 // Otherwise, start the recents activity
                 ActivityManager.RunningTaskInfo runningTask =
                         ActivityManagerWrapper.getInstance().getRunningTask();
-                startRecentsActivity(runningTask, isHomeStackVisible.value, true /* animate */,
-                        growTarget);
+                startRecentsActivityAndDismissKeyguardIfNeeded(runningTask,
+                        isHomeStackVisible.value, true /* animate */, growTarget);
 
                 // Only close the other system windows if we are actually showing recents
                 ActivityManagerWrapper.getInstance().closeSystemWindows(
@@ -462,6 +467,12 @@
             return;
         }
 
+        // Skip preloading recents when keyguard is showing
+        final StatusBar statusBar = getStatusBar();
+        if (statusBar != null && statusBar.isKeyguardShowing()) {
+            return;
+        }
+
         // Preload only the raw task list into a new load plan (which will be consumed by the
         // RecentsActivity) only if there is a task to animate to.  Post this to ensure that we
         // don't block the touch feedback on the nav bar button which triggers this.
@@ -942,9 +953,27 @@
     }
 
     /**
-     * Shows the recents activity
+     * Shows the recents activity after dismissing the keyguard if visible
      */
-    protected void startRecentsActivity(ActivityManager.RunningTaskInfo runningTask,
+    protected void startRecentsActivityAndDismissKeyguardIfNeeded(
+            final ActivityManager.RunningTaskInfo runningTask, final boolean isHomeStackVisible,
+            final boolean animate, final int growTarget) {
+        // Preload only if device for current user is unlocked
+        final StatusBar statusBar = getStatusBar();
+        if (statusBar != null && statusBar.isKeyguardShowing()) {
+            statusBar.executeRunnableDismissingKeyguard(() -> {
+                    // Flush trustmanager before checking device locked per user when preloading
+                    mTrustManager.reportKeyguardShowingChanged();
+                    mHandler.post(() -> startRecentsActivity(runningTask, isHomeStackVisible,
+                            animate, growTarget));
+                }, null,  true /* dismissShade */, false /* afterKeyguardGone */,
+                true /* deferred */);
+        } else {
+            startRecentsActivity(runningTask, isHomeStackVisible, animate, growTarget);
+        }
+    }
+
+    private void startRecentsActivity(ActivityManager.RunningTaskInfo runningTask,
             boolean isHomeStackVisible, boolean animate, int growTarget) {
         RecentsTaskLoader loader = Recents.getTaskLoader();
         RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
@@ -1033,6 +1062,10 @@
         return result;
     }
 
+    private StatusBar getStatusBar() {
+        return ((SystemUIApplication) mContext).getComponent(StatusBar.class);
+    }
+
     /**
      * Starts the recents activity.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index c281379..b71ebfd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import android.app.ActivityManager;
 import android.content.Context;
 import android.os.Handler;
 import android.os.UserHandle;
@@ -122,6 +121,8 @@
 
         // Split up the work over multiple frames.
         DejankUtils.postAfterTraversal(mShowRunnable);
+
+        mCallback.onBouncerVisiblityChanged(true /* shown */);
     }
 
     private final Runnable mShowRunnable = new Runnable() {
@@ -182,6 +183,7 @@
             mDismissCallbackRegistry.notifyDismissCancelled();
         }
         mFalsingManager.onBouncerHidden();
+        mCallback.onBouncerVisiblityChanged(false /* shown */);
         cancelShowRunnable();
         if (mKeyguardView != null) {
             mKeyguardView.cancelDismissAction();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
index 01679dd..ad31cc5 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityClientConnection.java
@@ -25,6 +25,7 @@
 import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -39,7 +40,9 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Process;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.KeyEvent;
@@ -93,6 +96,7 @@
     protected final Object mLock;
 
     protected final SecurityPolicy mSecurityPolicy;
+    private final AppOpsManager mAppOpsManager;
 
     // The service that's bound to this instance. Whenever this value is non-null, this
     // object is registered as a death recipient
@@ -250,6 +254,7 @@
         mAccessibilityServiceInfo = accessibilityServiceInfo;
         mLock = lock;
         mSecurityPolicy = securityPolicy;
+        mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
         mGlobalActionPerformer = globalActionPerfomer;
         mSystemSupport = systemSupport;
         mInvocationHandler = new InvocationHandler(mainHandler.getLooper());
@@ -706,6 +711,16 @@
             long accessibilityNodeId, int action, Bundle arguments, int interactionId,
             IAccessibilityInteractionConnectionCallback callback, long interrogatingTid)
             throws RemoteException {
+
+        // Skip this if the caller is the Accessibility InteractionBridge.
+        if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
+            if (mAppOpsManager.noteOp(AppOpsManager.OP_PERFORM_ACCESSIBILITY_ACTION,
+                    Binder.getCallingUid(), mComponentName.getPackageName())
+                    != AppOpsManager.MODE_ALLOWED) {
+                return false;
+            }
+        }
+
         final int resolvedWindowId;
         IAccessibilityInteractionConnection connection = null;
         synchronized (mLock) {
@@ -725,6 +740,15 @@
 
     @Override
     public boolean performGlobalAction(int action) {
+        // Skip this if the caller is the Accessibility InteractionBridge.
+        if (UserHandle.getAppId(Binder.getCallingUid()) != Process.SYSTEM_UID) {
+            if (mAppOpsManager.noteOp(AppOpsManager.OP_PERFORM_ACCESSIBILITY_ACTION,
+                    Binder.getCallingUid(), mComponentName.getPackageName())
+                    != AppOpsManager.MODE_ALLOWED) {
+                return false;
+            }
+        }
+
         synchronized (mLock) {
             if (!isCalledForCurrentUserLocked()) {
                 return false;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 5ec3850..270a762 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -772,7 +772,6 @@
     public void unregisterUiTestAutomationService(IAccessibilityServiceClient serviceClient) {
         synchronized (mLock) {
             mUiAutomationManager.unregisterUiTestAutomationServiceLocked(serviceClient);
-            onUserStateChangedLocked(getCurrentUserStateLocked());
         }
     }
 
@@ -2985,11 +2984,14 @@
         }
 
         private boolean isValidPackageForUid(String packageName, int uid) {
+            final long token = Binder.clearCallingIdentity();
             try {
                 return uid == mPackageManager.getPackageUidAsUser(
                         packageName, UserHandle.getUserId(uid));
             } catch (PackageManager.NameNotFoundException e) {
                 return false;
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
index f057112..56a9534 100644
--- a/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/UiAutomationManager.java
@@ -28,6 +28,7 @@
 import android.util.Slog;
 import android.view.accessibility.AccessibilityEvent;
 
+import com.android.internal.util.DumpUtils;
 import com.android.server.wm.WindowManagerInternal;
 
 import java.io.FileDescriptor;
@@ -45,6 +46,8 @@
 
     private AccessibilityServiceInfo mUiAutomationServiceInfo;
 
+    private AccessibilityClientConnection.SystemSupport mSystemSupport;
+
     private int mUiAutomationFlags;
 
     private IBinder mUiAutomationServiceOwner;
@@ -92,6 +95,7 @@
             return;
         }
 
+        mSystemSupport = systemSupport;
         mUiAutomationService = new UiAutomationService(context, accessibilityServiceInfo, id,
                 mainHandler, lock, securityPolicy, systemSupport, windowManagerInternal,
                 globalActionPerfomer);
@@ -169,6 +173,7 @@
             mUiAutomationServiceOwner.unlinkToDeath(mUiAutomationServiceOwnerDeathRecipient, 0);
             mUiAutomationServiceOwner = null;
         }
+        mSystemSupport.onClientChange(false);
     }
 
     private class UiAutomationService extends AccessibilityClientConnection {
@@ -224,6 +229,17 @@
             return true;
         }
 
+        @Override
+        public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
+            if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
+            synchronized (mLock) {
+                pw.append("Ui Automation[eventTypes="
+                        + AccessibilityEvent.eventTypeToString(mEventTypes));
+                pw.append(", notificationTimeout=" + mNotificationTimeout);
+                pw.append("]");
+            }
+        }
+
         // Since this isn't really an accessibility service, several methods are just stubbed here.
         @Override
         public boolean setSoftKeyboardShowMode(int mode) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 56f5f64..ceae93c 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -512,7 +512,6 @@
         }
 
         final AutofillId[] fieldClassificationIds = response.getFieldClassificationIds();
-        // TODO(b/67867469): remove once feature is finished (or use method from AFM to check)
         if (fieldClassificationIds != null && !mService.isFieldClassificationEnabled()) {
             Slog.w(TAG, "Ignoring " + response + " because field detection is disabled");
             processNullResponseLocked(requestFlags);
@@ -1662,7 +1661,7 @@
                         isIgnored ? ViewState.STATE_IGNORED : ViewState.STATE_INITIAL);
                 mViewStates.put(id, viewState);
 
-                // TODO(b/67867469): for optimization purposes, should also ignore if change is
+                // TODO(b/70407264): for optimization purposes, should also ignore if change is
                 // detectable, and batch-send them when the session is finished (but that will
                 // require tracking detectable fields on AutofillManager)
                 if (isIgnored) {
diff --git a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
index 4adcb99..94b06b6 100644
--- a/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/RefactoredBackupManagerService.java
@@ -25,7 +25,6 @@
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_OPERATION_TIMEOUT;
 import static com.android.server.backup.internal.BackupHandler.MSG_RESTORE_SESSION_TIMEOUT;
 import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_CLEAR;
-import static com.android.server.backup.internal.BackupHandler.MSG_RETRY_INIT;
 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_BACKUP;
 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_ADB_RESTORE;
 import static com.android.server.backup.internal.BackupHandler.MSG_RUN_CLEAR;
@@ -120,6 +119,7 @@
 import com.android.server.backup.restore.ActiveRestoreSession;
 import com.android.server.backup.restore.PerformUnifiedRestoreTask;
 import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportNotRegisteredException;
 import com.android.server.backup.utils.AppBackupUtils;
 import com.android.server.backup.utils.BackupManagerMonitorUtils;
 import com.android.server.backup.utils.BackupObserverUtils;
@@ -1083,56 +1083,35 @@
         return mBackupPasswordManager.backupPasswordMatches(currentPw);
     }
 
-    // Maintain persistent state around whether need to do an initialize operation.
-    // Must be called with the queue lock held.
-    public void recordInitPendingLocked(boolean isPending, String transportName) {
+    /**
+     * Maintain persistent state around whether need to do an initialize operation.
+     * Must be called with the queue lock held.
+     */
+    @GuardedBy("mQueueLock")
+    public void recordInitPendingLocked(
+            boolean isPending, String transportName, String transportDirName) {
         if (MORE_DEBUG) {
             Slog.i(TAG, "recordInitPendingLocked: " + isPending
                     + " on transport " + transportName);
         }
-        mBackupHandler.removeMessages(MSG_RETRY_INIT);
 
-        try {
-            IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
-            if (transport != null) {
-                String transportDirName = transport.transportDirName();
-                File stateDir = new File(mBaseStateDir, transportDirName);
-                File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
+        File stateDir = new File(mBaseStateDir, transportDirName);
+        File initPendingFile = new File(stateDir, INIT_SENTINEL_FILE_NAME);
 
-                if (isPending) {
-                    // We need an init before we can proceed with sending backup data.
-                    // Record that with an entry in our set of pending inits, as well as
-                    // journaling it via creation of a sentinel file.
-                    mPendingInits.add(transportName);
-                    try {
-                        (new FileOutputStream(initPendingFile)).close();
-                    } catch (IOException ioe) {
-                        // Something is badly wrong with our permissions; just try to move on
-                    }
-                } else {
-                    // No more initialization needed; wipe the journal and reset our state.
-                    initPendingFile.delete();
-                    mPendingInits.remove(transportName);
-                }
-                return; // done; don't fall through to the error case
-            }
-        } catch (Exception e) {
-            // transport threw when asked its name; fall through to the lookup-failed case
-            Slog.e(TAG, "Transport " + transportName + " failed to report name: "
-                    + e.getMessage());
-        }
-
-        // The named transport doesn't exist or threw.  This operation is
-        // important, so we record the need for a an init and post a message
-        // to retry the init later.
         if (isPending) {
+            // We need an init before we can proceed with sending backup data.
+            // Record that with an entry in our set of pending inits, as well as
+            // journaling it via creation of a sentinel file.
             mPendingInits.add(transportName);
-            mBackupHandler.sendMessageDelayed(
-                    mBackupHandler.obtainMessage(MSG_RETRY_INIT,
-                            (isPending ? 1 : 0),
-                            0,
-                            transportName),
-                    TRANSPORT_RETRY_INTERVAL);
+            try {
+                (new FileOutputStream(initPendingFile)).close();
+            } catch (IOException ioe) {
+                // Something is badly wrong with our permissions; just try to move on
+            }
+        } else {
+            // No more initialization needed; wipe the journal and reset our state.
+            initPendingFile.delete();
+            mPendingInits.remove(transportName);
         }
     }
 
@@ -1614,27 +1593,9 @@
             return BackupManager.ERROR_BACKUP_NOT_ALLOWED;
         }
 
-        // We're using pieces of the new binding on-demand infra-structure and the old always-bound
-        // infra-structure below this comment. The TransportManager.getCurrentTransportClient() line
-        // is using the new one and TransportManager.getCurrentTransportBinder() is using the old.
-        // This is weird but there is a reason.
-        // This is the natural place to put TransportManager.getCurrentTransportClient() because of
-        // the null handling below that should be the same for TransportClient.
-        // TransportClient.connect() would return a IBackupTransport for us (instead of using the
-        // old infra), but it may block and we don't want this in this thread.
-        // The only usage of transport in this method is for transport.transportDirName(). When the
-        // push-from-transport part of binding on-demand is in place we will replace the calls for
-        // IBackupTransport.transportDirName() with calls for
-        // TransportManager.transportDirName(transportName) or similar. So we'll leave the old piece
-        // here until we implement that.
-        // TODO(brufino): Remove always-bound code mTransportManager.getCurrentTransportBinder()
         TransportClient transportClient =
                 mTransportManager.getCurrentTransportClient("BMS.requestBackup()");
-        IBackupTransport transport = mTransportManager.getCurrentTransportBinder();
-        if (transportClient == null || transport == null) {
-            if (transportClient != null) {
-                mTransportManager.disposeOfTransportClient(transportClient, "BMS.requestBackup()");
-            }
+        if (transportClient == null) {
             BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
             monitor = BackupManagerMonitorUtils.monitorEvent(monitor,
                     BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL,
@@ -1679,15 +1640,7 @@
                     + " k/v backups");
         }
 
-        String dirName;
-        try {
-            dirName = transport.transportDirName();
-        } catch (Exception e) {
-            Slog.e(TAG, "Transport unavailable while attempting backup: " + e.getMessage());
-            BackupObserverUtils.sendBackupFinished(observer, BackupManager.ERROR_TRANSPORT_ABORTED);
-            return BackupManager.ERROR_TRANSPORT_ABORTED;
-        }
-
+        String dirName = transportClient.getTransportDirName();
         boolean nonIncrementalBackup = (flags & BackupManager.FLAG_NON_INCREMENTAL_BACKUP) != 0;
 
         Message msg = mBackupHandler.obtainMessage(MSG_REQUEST_BACKUP);
@@ -1998,16 +1951,17 @@
         writeFullBackupScheduleAsync();
     }
 
-    private boolean fullBackupAllowable(IBackupTransport transport) {
-        if (transport == null) {
-            Slog.w(TAG, "Transport not present; full data backup not performed");
+    private boolean fullBackupAllowable(String transportName) {
+        if (!mTransportManager.isTransportRegistered(transportName)) {
+            Slog.w(TAG, "Transport not registered; full data backup not performed");
             return false;
         }
 
         // Don't proceed unless we have already established package metadata
         // for the current dataset via a key/value backup pass.
         try {
-            File stateDir = new File(mBaseStateDir, transport.transportDirName());
+            String transportDirName = mTransportManager.getTransportDirName(transportName);
+            File stateDir = new File(mBaseStateDir, transportDirName);
             File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
             if (pmState.length() <= 0) {
                 if (DEBUG) {
@@ -2097,7 +2051,8 @@
 
                 headBusy = false;
 
-                if (!fullBackupAllowable(mTransportManager.getCurrentTransportBinder())) {
+                String transportName = mTransportManager.getCurrentTransportName();
+                if (!fullBackupAllowable(transportName)) {
                     if (MORE_DEBUG) {
                         Slog.i(TAG, "Preconditions not met; not running full backup");
                     }
@@ -2545,7 +2500,8 @@
             throw new IllegalStateException("Restore supported only for the device owner");
         }
 
-        if (!fullBackupAllowable(mTransportManager.getCurrentTransportBinder())) {
+        String transportName = mTransportManager.getCurrentTransportName();
+        if (!fullBackupAllowable(transportName)) {
             Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
         } else {
             if (DEBUG) {
@@ -2826,10 +2782,30 @@
                     if (wasEnabled && mProvisioned) {
                         // NOTE: we currently flush every registered transport, not just
                         // the currently-active one.
-                        String[] allTransports = mTransportManager.getBoundTransportNames();
+                        List<String> transportNames = new ArrayList<>();
+                        List<String> transportDirNames = new ArrayList<>();
+                        mTransportManager.forEachRegisteredTransport(
+                                name -> {
+                                    final String dirName;
+                                    try {
+                                        dirName =
+                                                mTransportManager
+                                                        .getTransportDirName(name);
+                                    } catch (TransportNotRegisteredException e) {
+                                        // Should never happen
+                                        Slog.e(TAG, "Unexpected unregistered transport", e);
+                                        return;
+                                    }
+                                    transportNames.add(name);
+                                    transportDirNames.add(dirName);
+                                });
+
                         // build the set of transports for which we are posting an init
-                        for (String transport : allTransports) {
-                            recordInitPendingLocked(true, transport);
+                        for (int i = 0; i < transportNames.size(); i++) {
+                            recordInitPendingLocked(
+                                    true,
+                                    transportNames.get(i),
+                                    transportDirNames.get(i));
                         }
                         mAlarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(),
                                 mRunInitIntent);
@@ -2993,7 +2969,7 @@
 
         final long oldId = Binder.clearCallingIdentity();
         try {
-            mTransportManager.describeTransport(
+            mTransportManager.updateTransportAttributes(
                     transportComponent,
                     name,
                     configurationIntent,
@@ -3093,23 +3069,16 @@
     public Intent getConfigurationIntent(String transportName) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "getConfigurationIntent");
-
-        final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
-        if (transport != null) {
-            try {
-                final Intent intent = transport.configurationIntent();
-                if (MORE_DEBUG) {
-                    Slog.d(TAG, "getConfigurationIntent() returning config intent "
-                            + intent);
-                }
-                return intent;
-            } catch (Exception e) {
-                /* fall through to return null */
-                Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
+        try {
+            Intent intent = mTransportManager.getTransportConfigurationIntent(transportName);
+            if (MORE_DEBUG) {
+                Slog.d(TAG, "getConfigurationIntent() returning intent " + intent);
             }
+            return intent;
+        } catch (TransportNotRegisteredException e) {
+            Slog.e(TAG, "Unable to get configuration intent from transport: " + e.getMessage());
+            return null;
         }
-
-        return null;
     }
 
     // Supply the configuration summary string for the given transport.  If the name is
@@ -3143,22 +3112,16 @@
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "getDataManagementIntent");
 
-        final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
-        if (transport != null) {
-            try {
-                final Intent intent = transport.dataManagementIntent();
-                if (MORE_DEBUG) {
-                    Slog.d(TAG, "getDataManagementIntent() returning intent "
-                            + intent);
-                }
-                return intent;
-            } catch (Exception e) {
-                /* fall through to return null */
-                Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
+        try {
+            Intent intent = mTransportManager.getTransportDataManagementIntent(transportName);
+            if (MORE_DEBUG) {
+                Slog.d(TAG, "getDataManagementIntent() returning intent " + intent);
             }
+            return intent;
+        } catch (TransportNotRegisteredException e) {
+            Slog.e(TAG, "Unable to get management intent from transport: " + e.getMessage());
+            return null;
         }
-
-        return null;
     }
 
     // Supply the menu label for affordances that fire the manage-data intent
@@ -3168,19 +3131,16 @@
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
                 "getDataManagementLabel");
 
-        final IBackupTransport transport = mTransportManager.getTransportBinder(transportName);
-        if (transport != null) {
-            try {
-                final String text = transport.dataManagementLabel();
-                if (MORE_DEBUG) Slog.d(TAG, "getDataManagementLabel() returning " + text);
-                return text;
-            } catch (Exception e) {
-                /* fall through to return null */
-                Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
+        try {
+            String label = mTransportManager.getTransportDataManagementLabel(transportName);
+            if (MORE_DEBUG) {
+                Slog.d(TAG, "getDataManagementLabel() returning " + label);
             }
+            return label;
+        } catch (TransportNotRegisteredException e) {
+            Slog.e(TAG, "Unable to get management label from transport: " + e.getMessage());
+            return null;
         }
-
-        return null;
     }
 
     // Callback: a requested backup agent has been instantiated.  This should only
@@ -3497,14 +3457,16 @@
             pw.println("Available transports:");
             final String[] transports = listAllTransports();
             if (transports != null) {
-                for (String t : listAllTransports()) {
+                for (String t : transports) {
                     pw.println((t.equals(mTransportManager.getCurrentTransportName()) ? "  * "
                             : "    ") + t);
                     try {
                         IBackupTransport transport = mTransportManager.getTransportBinder(t);
-                        File dir = new File(mBaseStateDir, transport.transportDirName());
+                        File dir = new File(mBaseStateDir,
+                                mTransportManager.getTransportDirName(t));
                         pw.println("       destination: " + transport.currentDestinationString());
-                        pw.println("       intent: " + transport.configurationIntent());
+                        pw.println("       intent: "
+                                + mTransportManager.getTransportConfigurationIntent(t));
                         for (File f : dir.listFiles()) {
                             pw.println(
                                     "       " + f.getName() + " - " + f.length() + " state bytes");
diff --git a/services/backup/java/com/android/server/backup/TransportManager.java b/services/backup/java/com/android/server/backup/TransportManager.java
index 1f3ebf9..f185443 100644
--- a/services/backup/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/java/com/android/server/backup/TransportManager.java
@@ -49,12 +49,14 @@
 import com.android.server.backup.transport.TransportClient;
 import com.android.server.backup.transport.TransportClientManager;
 import com.android.server.backup.transport.TransportConnectionListener;
+import com.android.server.backup.transport.TransportNotRegisteredException;
 
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 
 /**
@@ -236,6 +238,72 @@
         return getTransportBinder(mCurrentTransportName);
     }
 
+    /**
+     * Retrieve the configuration intent of {@code transportName}.
+     * @throws TransportNotRegisteredException if the transport is not registered.
+     */
+    @Nullable
+    public Intent getTransportConfigurationIntent(String transportName)
+            throws TransportNotRegisteredException {
+        synchronized (mTransportLock) {
+            return getRegisteredTransportDescriptionOrThrowLocked(transportName)
+                    .configurationIntent;
+        }
+    }
+
+    /**
+     * Retrieve the data management intent of {@code transportName}.
+     * @throws TransportNotRegisteredException if the transport is not registered.
+     */
+    @Nullable
+    public Intent getTransportDataManagementIntent(String transportName)
+            throws TransportNotRegisteredException {
+        synchronized (mTransportLock) {
+            return getRegisteredTransportDescriptionOrThrowLocked(transportName)
+                    .dataManagementIntent;
+        }
+    }
+
+    /**
+     * Retrieve the data management label of {@code transportName}.
+     * @throws TransportNotRegisteredException if the transport is not registered.
+     */
+    @Nullable
+    public String getTransportDataManagementLabel(String transportName)
+            throws TransportNotRegisteredException {
+        synchronized (mTransportLock) {
+            return getRegisteredTransportDescriptionOrThrowLocked(transportName)
+                    .dataManagementLabel;
+        }
+    }
+
+    /**
+     * Retrieve the transport dir name of {@code transportName}.
+     * @throws TransportNotRegisteredException if the transport is not registered.
+     */
+    public String getTransportDirName(String transportName)
+            throws TransportNotRegisteredException {
+        synchronized (mTransportLock) {
+            return getRegisteredTransportDescriptionOrThrowLocked(transportName)
+                    .transportDirName;
+        }
+    }
+
+    /**
+     * Execute {@code transportConsumer} for each registered transport passing the transport name.
+     * This is called with an internal lock held, ensuring that the transport will remain registered
+     * while {@code transportConsumer} is being executed. Don't do heavy operations in
+     * {@code transportConsumer}.
+     */
+    public void forEachRegisteredTransport(Consumer<String> transportConsumer) {
+        synchronized (mTransportLock) {
+            for (TransportDescription transportDescription
+                    : mRegisteredTransportsDescriptionMap.values()) {
+                transportConsumer.accept(transportDescription.name);
+            }
+        }
+    }
+
     public String getTransportName(IBackupTransport binder) {
         synchronized (mTransportLock) {
             for (TransportConnection conn : mValidTransports.values()) {
@@ -281,6 +349,17 @@
     }
 
     @GuardedBy("mTransportLock")
+    private TransportDescription getRegisteredTransportDescriptionOrThrowLocked(
+            String transportName) throws TransportNotRegisteredException {
+        TransportDescription description = getRegisteredTransportDescriptionLocked(transportName);
+        if (description == null) {
+            throw new TransportNotRegisteredException(transportName);
+        }
+        return description;
+    }
+
+
+    @GuardedBy("mTransportLock")
     @Nullable
     private Map.Entry<ComponentName, TransportDescription> getRegisteredTransportEntryLocked(
             String transportName) {
@@ -385,13 +464,13 @@
      * Updates given values for the transport already registered and identified with
      * {@param transportComponent}. If the transport is not registered it will log and return.
      */
-    public void describeTransport(
+    public void updateTransportAttributes(
             ComponentName transportComponent,
             String name,
             @Nullable Intent configurationIntent,
             String currentDestinationString,
             @Nullable Intent dataManagementIntent,
-            String dataManagementLabel) {
+            @Nullable String dataManagementLabel) {
         synchronized (mTransportLock) {
             TransportDescription description =
                     mRegisteredTransportsDescriptionMap.get(transportComponent);
@@ -766,7 +845,7 @@
         @Nullable private Intent configurationIntent;
         private String currentDestinationString;
         @Nullable private Intent dataManagementIntent;
-        private String dataManagementLabel;
+        @Nullable private String dataManagementLabel;
 
         private TransportDescription(
                 String name,
@@ -774,7 +853,7 @@
                 @Nullable Intent configurationIntent,
                 String currentDestinationString,
                 @Nullable Intent dataManagementIntent,
-                String dataManagementLabel) {
+                @Nullable String dataManagementLabel) {
             this.name = name;
             this.transportDirName = transportDirName;
             this.configurationIntent = configurationIntent;
diff --git a/services/backup/java/com/android/server/backup/internal/BackupHandler.java b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
index 4c78348..f29a9c2 100644
--- a/services/backup/java/com/android/server/backup/internal/BackupHandler.java
+++ b/services/backup/java/com/android/server/backup/internal/BackupHandler.java
@@ -293,16 +293,6 @@
                 break;
             }
 
-            case MSG_RETRY_INIT: {
-                synchronized (backupManagerService.getQueueLock()) {
-                    backupManagerService.recordInitPendingLocked(msg.arg1 != 0, (String) msg.obj);
-                    backupManagerService.getAlarmManager().set(AlarmManager.RTC_WAKEUP,
-                            System.currentTimeMillis(),
-                            backupManagerService.getRunInitIntent());
-                }
-                break;
-            }
-
             case MSG_RUN_GET_RESTORE_SETS: {
                 // Like other async operations, this is entered with the wakelock held
                 RestoreSet[] sets = null;
diff --git a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
index 690922f..b21b072 100644
--- a/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
+++ b/services/backup/java/com/android/server/backup/internal/PerformInitializeTask.java
@@ -98,7 +98,8 @@
                                     transportDirName));
                     EventLog.writeEvent(EventLogTags.BACKUP_SUCCESS, 0, millis);
                     synchronized (backupManagerService.getQueueLock()) {
-                        backupManagerService.recordInitPendingLocked(false, transportName);
+                        backupManagerService.recordInitPendingLocked(
+                                false, transportName, transportDirName);
                     }
                     notifyResult(transportName, BackupTransport.TRANSPORT_OK);
                 } else {
@@ -107,7 +108,8 @@
                     Slog.e(TAG, "Transport error in initializeDevice()");
                     EventLog.writeEvent(EventLogTags.BACKUP_TRANSPORT_FAILURE, "(initialize)");
                     synchronized (backupManagerService.getQueueLock()) {
-                        backupManagerService.recordInitPendingLocked(true, transportName);
+                        backupManagerService.recordInitPendingLocked(
+                                true, transportName, transportDirName);
                     }
                     notifyResult(transportName, status);
                     result = status;
diff --git a/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java b/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java
new file mode 100644
index 0000000..26bf92c
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/transport/TransportNotRegisteredException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.backup.transport;
+
+import android.util.AndroidException;
+
+import com.android.server.backup.TransportManager;
+
+/**
+ * Exception thrown when the transport is not registered.
+ *
+ * @see TransportManager#getTransportDirName(String)
+ * @see TransportManager#getTransportConfigurationIntent(String)
+ * @see TransportManager#getTransportDataManagementIntent(String)
+ * @see TransportManager#getTransportDataManagementLabel(String)
+ */
+public class TransportNotRegisteredException extends AndroidException {
+    public TransportNotRegisteredException(String transportName) {
+        super("Transport " + transportName + " not registered");
+    }
+}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index a2505a8..9fb2681 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -31,6 +31,7 @@
     static_libs: [
         "time_zone_distro",
         "time_zone_distro_installer",
+        "android.hardware.broadcastradio-V2.0-java",
         "android.hardware.health-V1.0-java",
         "android.hardware.health-V2.0-java",
         "android.hardware.weaver-V1.0-java",
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 924e736..04d292f 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -421,7 +421,9 @@
         boolean logOutlier = false;
         long dischargeDuration = 0;
 
-        mBatteryLevelCritical = (mHealthInfo.batteryLevel <= mCriticalBatteryLevel);
+        mBatteryLevelCritical =
+            mHealthInfo.batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
+            && mHealthInfo.batteryLevel <= mCriticalBatteryLevel;
         if (mHealthInfo.chargerAcOnline) {
             mPlugType = BatteryManager.BATTERY_PLUGGED_AC;
         } else if (mHealthInfo.chargerUsbOnline) {
@@ -509,6 +511,8 @@
             if (!mBatteryLevelLow) {
                 // Should we now switch in to low battery mode?
                 if (mPlugType == BATTERY_PLUGGED_NONE
+                        && mHealthInfo.batteryStatus !=
+                           BatteryManager.BATTERY_STATUS_UNKNOWN
                         && mHealthInfo.batteryLevel <= mLowBatteryWarningLevel) {
                     mBatteryLevelLow = true;
                 }
diff --git a/services/core/java/com/android/server/EntropyMixer.java b/services/core/java/com/android/server/EntropyMixer.java
index 24d8d1e..9877717 100644
--- a/services/core/java/com/android/server/EntropyMixer.java
+++ b/services/core/java/com/android/server/EntropyMixer.java
@@ -70,7 +70,10 @@
     /**
      * Handler that periodically updates the entropy on disk.
      */
-    private final Handler mHandler = new Handler() {
+    private final Handler mHandler = new Handler(IoThread.getHandler().getLooper()) {
+        // IMPLEMENTATION NOTE: This handler runs on the I/O thread to avoid I/O on the main thread.
+        // The reason we're using our own Handler instead of IoThread.getHandler() is to create our
+        // own ID space for the "what" parameter of messages seen by the handler.
         @Override
         public void handleMessage(Message msg) {
             if (msg.what != ENTROPY_WHAT) {
@@ -115,7 +118,12 @@
         IntentFilter broadcastFilter = new IntentFilter(Intent.ACTION_SHUTDOWN);
         broadcastFilter.addAction(Intent.ACTION_POWER_CONNECTED);
         broadcastFilter.addAction(Intent.ACTION_REBOOT);
-        context.registerReceiver(mBroadcastReceiver, broadcastFilter);
+        context.registerReceiver(
+                mBroadcastReceiver,
+                broadcastFilter,
+                null, // do not require broadcaster to hold any permissions
+                mHandler // process received broadcasts on the I/O thread instead of the main thread
+                );
     }
 
     private void scheduleEntropyWriter() {
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 25c3fe4..880e1e4 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -35,6 +35,7 @@
 import com.android.internal.os.TransferPipe;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethod;
 import com.android.internal.view.IInputMethodClient;
@@ -4663,23 +4664,31 @@
         @ShellCommandResult
         @Override
         public int onCommand(@Nullable String cmd) {
-            if (cmd == null) {
-                return handleDefaultCommands(cmd);
+            // For existing "adb shell ime <command>".
+            if ("ime".equals(cmd)) {
+                final String imeCommand = getNextArg();
+                if (imeCommand == null || "help".equals(imeCommand) || "-h".equals(imeCommand)) {
+                    onImeCommandHelp();
+                    return ShellCommandResult.SUCCESS;
+                }
+                switch (imeCommand) {
+                    case "list":
+                        return mService.handleShellCommandListInputMethods(this);
+                    case "enable":
+                        return mService.handleShellCommandEnableDisableInputMethod(this, true);
+                    case "disable":
+                        return mService.handleShellCommandEnableDisableInputMethod(this, false);
+                    case "set":
+                        return mService.handleShellCommandSetInputMethod(this);
+                    case "reset":
+                        return mService.handleShellCommandResetInputMethod(this);
+                    default:
+                        getOutPrintWriter().println("Unknown command: " + imeCommand);
+                        return ShellCommandResult.FAILURE;
+                }
             }
-            switch (cmd) {
-                case "list":
-                    return mService.handleShellCommandListInputMethods(this);
-                case "enable":
-                    return mService.handleShellCommandEnableDisableInputMethod(this, true);
-                case "disable":
-                    return mService.handleShellCommandEnableDisableInputMethod(this, false);
-                case "set":
-                    return mService.handleShellCommandSetInputMethod(this);
-                case "reset-ime":
-                    return mService.handleShellCommandResetInputMethod(this);
-                default:
-                    return handleDefaultCommands(cmd);
-            }
+
+            return handleDefaultCommands(cmd);
         }
 
         @BinderThread
@@ -4691,19 +4700,48 @@
                 pw.println("    Prints this help text.");
                 pw.println("  dump [options]");
                 pw.println("    Synonym of dumpsys.");
-                pw.println("  list [-a] [-s]");
-                pw.println("    prints all enabled input methods.");
-                pw.println("     -a: see all input methods");
-                pw.println("     -s: only a single summary line of each");
-                pw.println("  enable <ID>");
-                pw.println("    allows the given input method ID to be used.");
-                pw.println("  disable <ID>");
-                pw.println("    disallows the given input method ID to be used.");
-                pw.println("  set <ID>");
-                pw.println("    switches to the given input method ID.");
-                pw.println("  reset-ime");
-                pw.println("    reset currently selected/enabled IMEs to the default ones as if");
-                pw.println("    the device is initially booted with the current locale.");
+                pw.println("  ime <command> [options]");
+                pw.println("    Manipulate IMEs.  Run \"ime help\" for details.");
+            }
+        }
+
+        private void onImeCommandHelp() {
+            try (IndentingPrintWriter pw =
+                         new IndentingPrintWriter(getOutPrintWriter(), "  ", 100)) {
+                pw.println("ime <command>:");
+                pw.increaseIndent();
+
+                pw.println("list [-a] [-s]");
+                pw.increaseIndent();
+                pw.println("prints all enabled input methods.");
+                pw.increaseIndent();
+                pw.println("-a: see all input methods");
+                pw.println("-s: only a single summary line of each");
+                pw.decreaseIndent();
+                pw.decreaseIndent();
+
+                pw.println("enable <ID>");
+                pw.increaseIndent();
+                pw.println("allows the given input method ID to be used.");
+                pw.decreaseIndent();
+
+                pw.println("disable <ID>");
+                pw.increaseIndent();
+                pw.println("disallows the given input method ID to be used.");
+                pw.decreaseIndent();
+
+                pw.println("set <ID>");
+                pw.increaseIndent();
+                pw.println("switches to the given input method ID.");
+                pw.decreaseIndent();
+
+                pw.println("reset");
+                pw.increaseIndent();
+                pw.println("reset currently selected/enabled IMEs to the default ones as if "
+                        + "the device is initially booted with the current locale.");
+                pw.decreaseIndent();
+
+                pw.decreaseIndent();
             }
         }
     }
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 2116d9a..e9eb3b3 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -827,15 +827,15 @@
         throw new IllegalArgumentException("Invalid Direction: " + direction);
     }
 
-    @Override
     /** Get a new SPI and maintain the reservation in the system server */
-    public synchronized IpSecSpiResponse reserveSecurityParameterIndex(
+    @Override
+    public synchronized IpSecSpiResponse allocateSecurityParameterIndex(
             int direction, String remoteAddress, int requestedSpi, IBinder binder)
             throws RemoteException {
         checkDirection(direction);
         checkInetAddress(remoteAddress);
         /* requestedSpi can be anything in the int range, so no check is needed. */
-        checkNotNull(binder, "Null Binder passed to reserveSecurityParameterIndex");
+        checkNotNull(binder, "Null Binder passed to allocateSecurityParameterIndex");
 
         UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid());
         int resourceId = mNextResourceId.getAndIncrement();
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 7f0b508..7ecb9ce 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -31,8 +31,11 @@
 import android.Manifest;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerInternal.ScreenObserver;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
+import android.app.KeyguardManager;
 import android.app.usage.StorageStatsManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -160,7 +163,8 @@
  * watch for and manage dynamically added storage, such as SD cards and USB mass
  * storage. Also decides how storage should be presented to users on the device.
  */
-class StorageManagerService extends IStorageManager.Stub implements Watchdog.Monitor {
+class StorageManagerService extends IStorageManager.Stub
+        implements Watchdog.Monitor, ScreenObserver {
 
     // Static direct instance pointer for the tightly-coupled idle service to use
     static StorageManagerService sSelf = null;
@@ -402,6 +406,7 @@
     private volatile boolean mSystemReady = false;
     private volatile boolean mBootCompleted = false;
     private volatile boolean mDaemonConnected = false;
+    private volatile boolean mSecureKeyguardShowing = true;
 
     private PackageManagerService mPms;
 
@@ -827,6 +832,7 @@
                     mVold.onUserStarted(userId);
                     mStoraged.onUserStarted(userId);
                 }
+                mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing);
             } catch (Exception e) {
                 Slog.wtf(TAG, e);
             }
@@ -878,6 +884,24 @@
         }
     }
 
+    @Override
+    public void onAwakeStateChanged(boolean isAwake) {
+        // Ignored
+    }
+
+    @Override
+    public void onKeyguardStateChanged(boolean isShowing) {
+        // Push down current secure keyguard status so that we ignore malicious
+        // USB devices while locked.
+        mSecureKeyguardShowing = isShowing
+                && mContext.getSystemService(KeyguardManager.class).isDeviceSecure();
+        try {
+            mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing);
+        } catch (Exception e) {
+            Slog.wtf(TAG, e);
+        }
+    }
+
     void runIdleMaintenance(Runnable callback) {
         mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback));
     }
@@ -1414,6 +1438,9 @@
     }
 
     private void systemReady() {
+        LocalServices.getService(ActivityManagerInternal.class)
+                .registerScreenObserver(this);
+
         mSystemReady = true;
         mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
     }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9366f6e..51976b1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -210,6 +210,7 @@
 import android.app.ActivityManager.StackInfo;
 import android.app.ActivityManager.TaskSnapshot;
 import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerInternal.ScreenObserver;
 import android.app.ActivityManagerInternal.SleepToken;
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
@@ -1627,6 +1628,8 @@
         }
     }
 
+    final List<ScreenObserver> mScreenObservers = new ArrayList<>();
+
     final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>();
     ProcessChangeItem[] mActiveProcessChanges = new ProcessChangeItem[5];
 
@@ -1749,13 +1752,13 @@
     static final int LOG_STACK_STATE = 60;
     static final int VR_MODE_CHANGE_MSG = 61;
     static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 63;
-    static final int NOTIFY_VR_SLEEPING_MSG = 65;
+    static final int DISPATCH_SCREEN_AWAKE_MSG = 64;
+    static final int DISPATCH_SCREEN_KEYGUARD_MSG = 65;
     static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66;
     static final int DISPATCH_PENDING_INTENT_CANCEL_MSG = 67;
     static final int PUSH_TEMP_WHITELIST_UI_MSG = 68;
     static final int SERVICE_FOREGROUND_CRASH_MSG = 69;
     static final int DISPATCH_OOM_ADJ_OBSERVER_MSG = 70;
-    static final int NOTIFY_VR_KEYGUARD_MSG = 74;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -2391,11 +2394,17 @@
                     }
                 }
             } break;
-            case NOTIFY_VR_SLEEPING_MSG: {
-                notifyVrManagerOfSleepState(msg.arg1 != 0);
+            case DISPATCH_SCREEN_AWAKE_MSG: {
+                final boolean isAwake = msg.arg1 != 0;
+                for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+                    mScreenObservers.get(i).onAwakeStateChanged(isAwake);
+                }
             } break;
-            case NOTIFY_VR_KEYGUARD_MSG: {
-                notifyVrManagerOfKeyguardState(msg.arg1 != 0);
+            case DISPATCH_SCREEN_KEYGUARD_MSG: {
+                final boolean isShowing = msg.arg1 != 0;
+                for (int i = mScreenObservers.size() - 1; i >= 0; i--) {
+                    mScreenObservers.get(i).onKeyguardStateChanged(isShowing);
+                }
             } break;
             case HANDLE_TRUST_STORAGE_UPDATE_MSG: {
                 synchronized (ActivityManagerService.this) {
@@ -2947,9 +2956,11 @@
         try {
             return super.onTransact(code, data, reply, flags);
         } catch (RuntimeException e) {
-            // The activity manager only throws security exceptions, so let's
+            // The activity manager only throws certain exceptions intentionally, so let's
             // log all others.
-            if (!(e instanceof SecurityException)) {
+            if (!(e instanceof SecurityException
+                    || e instanceof IllegalArgumentException
+                    || e instanceof IllegalStateException)) {
                 Slog.wtf(TAG, "Activity Manager Crash."
                         + " UID:" + Binder.getCallingUid()
                         + " PID:" + Binder.getCallingPid()
@@ -3292,32 +3303,6 @@
                 mHandler.obtainMessage(VR_MODE_CHANGE_MSG, 0, 0, r));
     }
 
-    private void sendNotifyVrManagerOfSleepState(boolean isSleeping) {
-        mHandler.sendMessage(
-                mHandler.obtainMessage(NOTIFY_VR_SLEEPING_MSG, isSleeping ? 1 : 0, 0));
-    }
-
-    private void notifyVrManagerOfSleepState(boolean isSleeping) {
-        final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
-        if (vrService == null) {
-            return;
-        }
-        vrService.onSleepStateChanged(isSleeping);
-    }
-
-    private void sendNotifyVrManagerOfKeyguardState(boolean isShowing) {
-        mHandler.sendMessage(
-                mHandler.obtainMessage(NOTIFY_VR_KEYGUARD_MSG, isShowing ? 1 : 0, 0));
-    }
-
-    private void notifyVrManagerOfKeyguardState(boolean isShowing) {
-        final VrManagerInternal vrService = LocalServices.getService(VrManagerInternal.class);
-        if (vrService == null) {
-            return;
-        }
-        vrService.onKeyguardStateChanged(isShowing);
-    }
-
     final void showAskCompatModeDialogLocked(ActivityRecord r) {
         Message msg = Message.obtain();
         msg.what = SHOW_COMPAT_MODE_DIALOG_UI_MSG;
@@ -12432,7 +12417,8 @@
             if (wasAwake != isAwake) {
                 // Also update state in a special way for running foreground services UI.
                 mServices.updateScreenStateLocked(isAwake);
-                sendNotifyVrManagerOfSleepState(!isAwake);
+                mHandler.obtainMessage(DISPATCH_SCREEN_AWAKE_MSG, isAwake ? 1 : 0, 0)
+                        .sendToTarget();
             }
         }
     }
@@ -12588,7 +12574,9 @@
                 Binder.restoreCallingIdentity(ident);
             }
         }
-        sendNotifyVrManagerOfKeyguardState(showing);
+
+        mHandler.obtainMessage(DISPATCH_SCREEN_KEYGUARD_MSG, showing ? 1 : 0, 0)
+                .sendToTarget();
     }
 
     @Override
@@ -24525,6 +24513,11 @@
             }
             return false;
         }
+
+        @Override
+        public void registerScreenObserver(ScreenObserver observer) {
+            mScreenObservers.add(observer);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 54938eb..4f60e17 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -48,6 +48,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.ShellCommand;
+import android.os.StrictMode;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -128,7 +129,7 @@
         if (cmd == null) {
             return handleDefaultCommands(cmd);
         }
-        PrintWriter pw = getOutPrintWriter();
+        final PrintWriter pw = getOutPrintWriter();
         try {
             switch (cmd) {
                 case "start":
@@ -1328,65 +1329,95 @@
         @Override
         public void onUidStateChanged(int uid, int procState, long procStateSeq) throws RemoteException {
             synchronized (this) {
-                mPw.print(uid);
-                mPw.print(" procstate ");
-                mPw.print(ProcessList.makeProcStateString(procState));
-                mPw.print(" seq ");
-                mPw.println(procStateSeq);
-                mPw.flush();
+                final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+                try {
+                    mPw.print(uid);
+                    mPw.print(" procstate ");
+                    mPw.print(ProcessList.makeProcStateString(procState));
+                    mPw.print(" seq ");
+                    mPw.println(procStateSeq);
+                    mPw.flush();
+                } finally {
+                    StrictMode.setThreadPolicy(oldPolicy);
+                }
             }
         }
 
         @Override
         public void onUidGone(int uid, boolean disabled) throws RemoteException {
             synchronized (this) {
-                mPw.print(uid);
-                mPw.print(" gone");
-                if (disabled) {
-                    mPw.print(" disabled");
+                final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+                try {
+                    mPw.print(uid);
+                    mPw.print(" gone");
+                    if (disabled) {
+                        mPw.print(" disabled");
+                    }
+                    mPw.println();
+                    mPw.flush();
+                } finally {
+                    StrictMode.setThreadPolicy(oldPolicy);
                 }
-                mPw.println();
-                mPw.flush();
             }
         }
 
         @Override
         public void onUidActive(int uid) throws RemoteException {
             synchronized (this) {
-                mPw.print(uid);
-                mPw.println(" active");
-                mPw.flush();
+                final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+                try {
+                    mPw.print(uid);
+                    mPw.println(" active");
+                    mPw.flush();
+                } finally {
+                    StrictMode.setThreadPolicy(oldPolicy);
+                }
             }
         }
 
         @Override
         public void onUidIdle(int uid, boolean disabled) throws RemoteException {
             synchronized (this) {
-                mPw.print(uid);
-                mPw.print(" idle");
-                if (disabled) {
-                    mPw.print(" disabled");
+                final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+                try {
+                    mPw.print(uid);
+                    mPw.print(" idle");
+                    if (disabled) {
+                        mPw.print(" disabled");
+                    }
+                    mPw.println();
+                    mPw.flush();
+                } finally {
+                    StrictMode.setThreadPolicy(oldPolicy);
                 }
-                mPw.println();
-                mPw.flush();
             }
         }
 
         @Override
         public void onUidCachedChanged(int uid, boolean cached) throws RemoteException {
             synchronized (this) {
-                mPw.print(uid);
-                mPw.println(cached ? " cached" : " uncached");
-                mPw.flush();
+                final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+                try {
+                    mPw.print(uid);
+                    mPw.println(cached ? " cached" : " uncached");
+                    mPw.flush();
+                } finally {
+                    StrictMode.setThreadPolicy(oldPolicy);
+                }
             }
         }
 
         @Override
         public void onOomAdjMessage(String msg) {
             synchronized (this) {
-                mPw.print("# ");
-                mPw.println(msg);
-                mPw.flush();
+                final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+                try {
+                    mPw.print("# ");
+                    mPw.println(msg);
+                    mPw.flush();
+                } finally {
+                    StrictMode.setThreadPolicy(oldPolicy);
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 0ffc6b8..91b3315 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -2254,7 +2254,7 @@
             String callingPackage = "";
             int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE;
             boolean supportsPictureInPicture = false;
-            Rect bounds = null;
+            Rect lastNonFullscreenBounds = null;
             int minWidth = INVALID_MIN_SIZE;
             int minHeight = INVALID_MIN_SIZE;
             int persistTaskVersion = 0;
@@ -2339,7 +2339,7 @@
                         supportsPictureInPicture = Boolean.parseBoolean(attrValue);
                         break;
                     case ATTR_NON_FULLSCREEN_BOUNDS:
-                        bounds = Rect.unflattenFromString(attrValue);
+                        lastNonFullscreenBounds = Rect.unflattenFromString(attrValue);
                         break;
                     case ATTR_MIN_WIDTH:
                         minWidth = Integer.parseInt(attrValue);
@@ -2434,7 +2434,8 @@
                     taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid,
                     callingPackage, resizeMode, supportsPictureInPicture, realActivitySuspended,
                     userSetupComplete, minWidth, minHeight);
-            task.updateOverrideConfiguration(bounds);
+            task.mLastNonFullscreenBounds = lastNonFullscreenBounds;
+            task.setBounds(lastNonFullscreenBounds);
 
             for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
                 activities.get(activityNdx).setTask(task);
diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
index 8fdbcf6c..3064144 100644
--- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.broadcastradio;
 
+import android.annotation.NonNull;
 import android.Manifest;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -28,14 +29,16 @@
 import com.android.server.SystemService;
 
 import java.util.List;
+import java.util.Objects;
+import java.util.OptionalInt;
 
 public class BroadcastRadioService extends SystemService {
     private final ServiceImpl mServiceImpl = new ServiceImpl();
 
-    /**
-     * This field is used by native code, do not access or modify.
-     */
-    private final long mNativeContext = nativeInit();
+    private final com.android.server.broadcastradio.hal1.BroadcastRadioService mHal1 =
+            new com.android.server.broadcastradio.hal1.BroadcastRadioService();
+    private final com.android.server.broadcastradio.hal2.BroadcastRadioService mHal2 =
+            new com.android.server.broadcastradio.hal2.BroadcastRadioService();
 
     private final Object mLock = new Object();
     private List<RadioManager.ModuleProperties> mModules = null;
@@ -45,22 +48,18 @@
     }
 
     @Override
-    protected void finalize() throws Throwable {
-        nativeFinalize(mNativeContext);
-        super.finalize();
-    }
-
-    private native long nativeInit();
-    private native void nativeFinalize(long nativeContext);
-    private native List<RadioManager.ModuleProperties> nativeLoadModules(long nativeContext);
-    private native Tuner nativeOpenTuner(long nativeContext, int moduleId,
-            RadioManager.BandConfig config, boolean withAudio, ITunerCallback callback);
-
-    @Override
     public void onStart() {
         publishBinderService(Context.RADIO_SERVICE, mServiceImpl);
     }
 
+    /**
+     * Finds next available index for newly loaded modules.
+     */
+    private static int getNextId(@NonNull List<RadioManager.ModuleProperties> modules) {
+        OptionalInt max = modules.stream().mapToInt(RadioManager.ModuleProperties::getId).max();
+        return max.isPresent() ? max.getAsInt() + 1 : 0;
+    }
+
     private class ServiceImpl extends IRadioService.Stub {
         private void enforcePolicyAccess() {
             if (PackageManager.PERMISSION_GRANTED != getContext().checkCallingPermission(
@@ -75,11 +74,8 @@
             synchronized (mLock) {
                 if (mModules != null) return mModules;
 
-                mModules = nativeLoadModules(mNativeContext);
-                if (mModules == null) {
-                    throw new ParcelableException(new NullPointerException(
-                            "couldn't load radio modules"));
-                }
+                mModules = mHal1.loadModules();
+                mModules.addAll(mHal2.loadModules(getNextId(mModules)));
 
                 return mModules;
             }
@@ -93,7 +89,11 @@
                 throw new IllegalArgumentException("Callback must not be empty");
             }
             synchronized (mLock) {
-                return nativeOpenTuner(mNativeContext, moduleId, bandConfig, withAudio, callback);
+                if (mHal2.hasModule(moduleId)) {
+                    throw new RuntimeException("Not implemented");
+                } else {
+                    return mHal1.openTuner(moduleId, bandConfig, withAudio, callback);
+                }
             }
         }
     }
diff --git a/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
new file mode 100644
index 0000000..e8ac547
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/hal1/BroadcastRadioService.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.broadcastradio.hal1;
+
+import android.annotation.NonNull;
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.radio.IRadioService;
+import android.hardware.radio.ITuner;
+import android.hardware.radio.ITunerCallback;
+import android.hardware.radio.RadioManager;
+import android.os.ParcelableException;
+
+import com.android.server.SystemService;
+
+import java.util.List;
+import java.util.Objects;
+
+public class BroadcastRadioService {
+    /**
+     * This field is used by native code, do not access or modify.
+     */
+    private final long mNativeContext = nativeInit();
+
+    private final Object mLock = new Object();
+
+    @Override
+    protected void finalize() throws Throwable {
+        nativeFinalize(mNativeContext);
+        super.finalize();
+    }
+
+    private native long nativeInit();
+    private native void nativeFinalize(long nativeContext);
+    private native List<RadioManager.ModuleProperties> nativeLoadModules(long nativeContext);
+    private native Tuner nativeOpenTuner(long nativeContext, int moduleId,
+            RadioManager.BandConfig config, boolean withAudio, ITunerCallback callback);
+
+    public @NonNull List<RadioManager.ModuleProperties> loadModules() {
+        synchronized (mLock) {
+            return Objects.requireNonNull(nativeLoadModules(mNativeContext));
+        }
+    }
+
+    public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig,
+            boolean withAudio, @NonNull ITunerCallback callback) {
+        synchronized (mLock) {
+            return nativeOpenTuner(mNativeContext, moduleId, bandConfig, withAudio, callback);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/broadcastradio/Convert.java b/services/core/java/com/android/server/broadcastradio/hal1/Convert.java
similarity index 97%
rename from services/core/java/com/android/server/broadcastradio/Convert.java
rename to services/core/java/com/android/server/broadcastradio/hal1/Convert.java
index 125554f..80c7762 100644
--- a/services/core/java/com/android/server/broadcastradio/Convert.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/Convert.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.broadcastradio;
+package com.android.server.broadcastradio.hal1;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/broadcastradio/Tuner.java b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
similarity index 99%
rename from services/core/java/com/android/server/broadcastradio/Tuner.java
rename to services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
index 2ea4271..cce534d 100644
--- a/services/core/java/com/android/server/broadcastradio/Tuner.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/Tuner.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.broadcastradio;
+package com.android.server.broadcastradio.hal1;
 
 import android.annotation.NonNull;
 import android.graphics.Bitmap;
diff --git a/services/core/java/com/android/server/broadcastradio/TunerCallback.java b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
similarity index 98%
rename from services/core/java/com/android/server/broadcastradio/TunerCallback.java
rename to services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
index 2460c67..673ff88 100644
--- a/services/core/java/com/android/server/broadcastradio/TunerCallback.java
+++ b/services/core/java/com/android/server/broadcastradio/hal1/TunerCallback.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.server.broadcastradio;
+package com.android.server.broadcastradio.hal1;
 
 import android.annotation.NonNull;
 import android.hardware.radio.ITuner;
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
new file mode 100644
index 0000000..7629477
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/hal2/BroadcastRadioService.java
@@ -0,0 +1,79 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.broadcastradio.hal2;
+
+import android.annotation.NonNull;
+import android.hardware.radio.RadioManager;
+import android.hardware.broadcastradio.V2_0.IBroadcastRadio;
+import android.hidl.manager.V1_0.IServiceManager;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class BroadcastRadioService {
+    private static final String TAG = "BcRadio2Srv";
+
+    private final Map<Integer, RadioModule> mModules = new HashMap<>();
+
+    private static @NonNull List<String> listByInterface(@NonNull String fqName) {
+        try {
+            IServiceManager manager = IServiceManager.getService();
+            if (manager == null) {
+                Slog.e(TAG, "Failed to get HIDL Service Manager");
+                return Collections.emptyList();
+            }
+
+            List<String> list = manager.listByInterface(fqName);
+            if (list == null) {
+                Slog.e(TAG, "Didn't get interface list from HIDL Service Manager");
+                return Collections.emptyList();
+            }
+            return list;
+        } catch (RemoteException ex) {
+            Slog.e(TAG, "Failed fetching interface list", ex);
+            return Collections.emptyList();
+        }
+    }
+
+    public @NonNull Collection<RadioManager.ModuleProperties> loadModules(int idx) {
+        Slog.v(TAG, "loadModules(" + idx + ")");
+
+        for (String serviceName : listByInterface(IBroadcastRadio.kInterfaceName)) {
+            Slog.v(TAG, "checking service: " + serviceName);
+
+            RadioModule module = RadioModule.tryLoadingModule(idx, serviceName);
+            if (module != null) {
+                Slog.i(TAG, "loaded broadcast radio module " + idx + ": " +
+                        serviceName + " (HAL 2.0)");
+                mModules.put(idx++, module);
+            }
+        }
+
+        return mModules.values().stream().map(module -> module.mProperties).
+                collect(Collectors.toList());
+    }
+
+    public boolean hasModule(int id) {
+        return mModules.containsKey(id);
+    }
+}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
new file mode 100644
index 0000000..c3394e9
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java
@@ -0,0 +1,132 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.broadcastradio.hal2;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.broadcastradio.V2_0.Properties;
+import android.hardware.broadcastradio.V2_0.VendorKeyValue;
+import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioManager;
+import android.util.Slog;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+class Convert {
+    private static final String TAG = "BcRadio2Srv.convert";
+
+    private static @NonNull Map<String, String>
+    vendorInfoFromHal(@Nullable List<VendorKeyValue> info) {
+        if (info == null) return Collections.emptyMap();
+
+        Map<String, String> map = new HashMap<>();
+        for (VendorKeyValue kvp : info) {
+            if (kvp.key == null || kvp.value == null) {
+                Slog.w(TAG, "VendorKeyValue contains null pointers");
+                continue;
+            }
+            map.put(kvp.key, kvp.value);
+        }
+
+        return map;
+    }
+
+    private static @NonNull int[]
+    identifierTypesToProgramTypes(@NonNull int[] idTypes) {
+        Set<Integer> pTypes = new HashSet<>();
+
+        for (int idType : idTypes) {
+            switch (idType) {
+                case ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY:
+                case ProgramSelector.IDENTIFIER_TYPE_RDS_PI:
+                    // TODO(b/69958423): verify AM/FM with region info
+                    pTypes.add(ProgramSelector.PROGRAM_TYPE_AM);
+                    pTypes.add(ProgramSelector.PROGRAM_TYPE_FM);
+                    break;
+                case ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT:
+                    // TODO(b/69958423): verify AM/FM with region info
+                    pTypes.add(ProgramSelector.PROGRAM_TYPE_AM_HD);
+                    pTypes.add(ProgramSelector.PROGRAM_TYPE_FM_HD);
+                    break;
+                case ProgramSelector.IDENTIFIER_TYPE_DAB_SIDECC:
+                case ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE:
+                case ProgramSelector.IDENTIFIER_TYPE_DAB_SCID:
+                case ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY:
+                    pTypes.add(ProgramSelector.PROGRAM_TYPE_DAB);
+                    break;
+                case ProgramSelector.IDENTIFIER_TYPE_DRMO_SERVICE_ID:
+                case ProgramSelector.IDENTIFIER_TYPE_DRMO_FREQUENCY:
+                    pTypes.add(ProgramSelector.PROGRAM_TYPE_DRMO);
+                    break;
+                case ProgramSelector.IDENTIFIER_TYPE_SXM_SERVICE_ID:
+                case ProgramSelector.IDENTIFIER_TYPE_SXM_CHANNEL:
+                    pTypes.add(ProgramSelector.PROGRAM_TYPE_SXM);
+                    break;
+                default:
+                    break;
+            }
+            if (idType >= ProgramSelector.IDENTIFIER_TYPE_VENDOR_PRIMARY_START
+                    && idType <= ProgramSelector.IDENTIFIER_TYPE_VENDOR_PRIMARY_END) {
+                pTypes.add(idType);
+            }
+        }
+
+        return pTypes.stream().mapToInt(Integer::intValue).toArray();
+    }
+
+    static @NonNull RadioManager.ModuleProperties
+    propertiesFromHal(int id, @NonNull String serviceName, Properties prop) {
+        Objects.requireNonNull(prop);
+
+        // TODO(b/69958423): implement region info
+        RadioManager.BandDescriptor[] bands = new RadioManager.BandDescriptor[0];
+
+        int[] supportedIdentifierTypes = prop.supportedIdentifierTypes.stream().
+                mapToInt(Integer::intValue).toArray();
+        int[] supportedProgramTypes = identifierTypesToProgramTypes(supportedIdentifierTypes);
+
+        return new RadioManager.ModuleProperties(
+                id,
+                serviceName,
+
+                // There is no Class concept in HAL 2.0.
+                RadioManager.CLASS_AM_FM,
+
+                prop.maker,
+                prop.product,
+                prop.version,
+                prop.serial,
+
+                /* HAL 2.0 only supports single tuner and audio source per
+                 * HAL implementation instance. */
+                1,      // numTuners
+                1,      // numAudioSources
+                false,  // isCaptureSupported
+
+                bands,
+                true,  // isBgScanSupported is deprecated
+                supportedProgramTypes,
+                supportedIdentifierTypes,
+                vendorInfoFromHal(prop.vendorInfo));
+    }
+}
diff --git a/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
new file mode 100644
index 0000000..34c1b0c
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/hal2/RadioModule.java
@@ -0,0 +1,54 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.broadcastradio.hal2;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.radio.RadioManager;
+import android.hardware.broadcastradio.V2_0.IBroadcastRadio;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.util.Objects;
+
+class RadioModule {
+    private static final String TAG = "BcRadio2Srv.module";
+
+    @NonNull private final IBroadcastRadio mService;
+    @NonNull public final RadioManager.ModuleProperties mProperties;
+
+    private RadioModule(@NonNull IBroadcastRadio service,
+            @NonNull RadioManager.ModuleProperties properties) {
+        mProperties = Objects.requireNonNull(properties);
+        mService = Objects.requireNonNull(service);
+    }
+
+    public static @Nullable RadioModule tryLoadingModule(int idx, @NonNull String fqName) {
+        try {
+            IBroadcastRadio service = IBroadcastRadio.getService();
+            if (service == null) return null;
+
+            RadioManager.ModuleProperties prop =
+                    Convert.propertiesFromHal(idx, fqName, service.getProperties());
+
+            return new RadioModule(service, prop);
+        } catch (RemoteException ex) {
+            Slog.e(TAG, "failed to load module " + fqName, ex);
+            return null;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 009f10e7..bcb57ef 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -279,8 +279,8 @@
         private static final long DEFAULT_MIN_LINEAR_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
         private static final long DEFAULT_MIN_EXP_BACKOFF_TIME = JobInfo.MIN_BACKOFF_MILLIS;
         private static final long DEFAULT_STANDBY_HEARTBEAT_TIME = 11 * 60 * 1000L;
-        private static final int DEFAULT_STANDBY_WORKING_BEATS = 5;  // ~ 1 hour, with 11-min beats
-        private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 31; // ~ 6 hours
+        private static final int DEFAULT_STANDBY_WORKING_BEATS = 11;  // ~ 2 hours, with 11min beats
+        private static final int DEFAULT_STANDBY_FREQUENT_BEATS = 43; // ~ 8 hours
         private static final int DEFAULT_STANDBY_RARE_BEATS = 130; // ~ 24 hours
 
         /**
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index 90c912a..1ad0cf9 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -642,11 +642,10 @@
     /**
      * Loads a nanoapp binary at the specified Context hub.
      *
-     * @param contextHubId the ID of the hub to load the binary
+     * @param contextHubId        the ID of the hub to load the binary
      * @param transactionCallback the client-facing transaction callback interface
-     * @param nanoAppBinary the binary to load
+     * @param nanoAppBinary       the binary to load
      *
-     * @throws RemoteException
      * @throws IllegalStateException if the transaction queue is full
      */
     @Override
@@ -673,11 +672,10 @@
     /**
      * Unloads a nanoapp from the specified Context Hub.
      *
-     * @param contextHubId the ID of the hub to unload the nanoapp
+     * @param contextHubId        the ID of the hub to unload the nanoapp
      * @param transactionCallback the client-facing transaction callback interface
-     * @param nanoAppId the ID of the nanoapp to unload
+     * @param nanoAppId           the ID of the nanoapp to unload
      *
-     * @throws RemoteException
      * @throws IllegalStateException if the transaction queue is full
      */
     @Override
@@ -696,12 +694,59 @@
     }
 
     /**
+     * Enables a nanoapp at the specified Context Hub.
+     *
+     * @param contextHubId        the ID of the hub to enable the nanoapp
+     * @param transactionCallback the client-facing transaction callback interface
+     * @param nanoAppId           the ID of the nanoapp to enable
+     *
+     * @throws IllegalStateException if the transaction queue is full
+     */
+    @Override
+    public void enableNanoApp(
+            int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
+            throws RemoteException {
+        checkPermissions();
+        if (!checkHalProxyAndContextHubId(
+                contextHubId, transactionCallback, ContextHubTransaction.TYPE_ENABLE_NANOAPP)) {
+            return;
+        }
+
+        ContextHubServiceTransaction transaction = mTransactionManager.createEnableTransaction(
+                contextHubId, nanoAppId, transactionCallback);
+        mTransactionManager.addTransaction(transaction);
+    }
+
+    /**
+     * Disables a nanoapp at the specified Context Hub.
+     *
+     * @param contextHubId        the ID of the hub to disable the nanoapp
+     * @param transactionCallback the client-facing transaction callback interface
+     * @param nanoAppId           the ID of the nanoapp to disable
+     *
+     * @throws IllegalStateException if the transaction queue is full
+     */
+    @Override
+    public void disableNanoApp(
+            int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
+            throws RemoteException {
+        checkPermissions();
+        if (!checkHalProxyAndContextHubId(
+                contextHubId, transactionCallback, ContextHubTransaction.TYPE_DISABLE_NANOAPP)) {
+            return;
+        }
+
+        ContextHubServiceTransaction transaction = mTransactionManager.createDisableTransaction(
+                contextHubId, nanoAppId, transactionCallback);
+        mTransactionManager.addTransaction(transaction);
+    }
+
+    /**
      * Queries for a list of nanoapps from the specified Context hub.
      *
-     * @param contextHubId the ID of the hub to query
+     * @param contextHubId        the ID of the hub to query
      * @param transactionCallback the client-facing transaction callback interface
      *
-     * @throws RemoteException
      * @throws IllegalStateException if the transaction queue is full
      */
     @Override
@@ -713,8 +758,8 @@
             return;
         }
 
-        ContextHubServiceTransaction transaction =
-                mTransactionManager.createQueryTransaction(contextHubId, transactionCallback);
+        ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
+                contextHubId, transactionCallback);
         mTransactionManager.addTransaction(transaction);
     }
 
diff --git a/services/core/java/com/android/server/location/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
index 00252bc..412d43d 100644
--- a/services/core/java/com/android/server/location/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/ContextHubTransactionManager.java
@@ -143,7 +143,7 @@
     /**
      * Creates a transaction for unloading a nanoapp.
      *
-     * @param contextHubId       the ID of the hub to load the nanoapp to
+     * @param contextHubId       the ID of the hub to unload the nanoapp from
      * @param nanoAppId          the ID of the nanoapp to unload
      * @param onCompleteCallback the client on complete callback
      * @return the generated transaction
@@ -182,6 +182,76 @@
     }
 
     /**
+     * Creates a transaction for enabling a nanoapp.
+     *
+     * @param contextHubId       the ID of the hub to enable the nanoapp on
+     * @param nanoAppId          the ID of the nanoapp to enable
+     * @param onCompleteCallback the client on complete callback
+     * @return the generated transaction
+     */
+    /* package */ ContextHubServiceTransaction createEnableTransaction(
+            int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback) {
+        return new ContextHubServiceTransaction(
+                mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_ENABLE_NANOAPP) {
+            @Override
+            /* package */ int onTransact() {
+                try {
+                    return mContextHubProxy.enableNanoApp(
+                            contextHubId, nanoAppId, this.getTransactionId());
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException while trying to enable nanoapp with ID 0x" +
+                            Long.toHexString(nanoAppId), e);
+                    return Result.UNKNOWN_FAILURE;
+                }
+            }
+
+            @Override
+            /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+                try {
+                    onCompleteCallback.onTransactionComplete(result);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException while calling client onTransactionComplete", e);
+                }
+            }
+        };
+    }
+
+    /**
+     * Creates a transaction for disabling a nanoapp.
+     *
+     * @param contextHubId       the ID of the hub to disable the nanoapp on
+     * @param nanoAppId          the ID of the nanoapp to disable
+     * @param onCompleteCallback the client on complete callback
+     * @return the generated transaction
+     */
+    /* package */ ContextHubServiceTransaction createDisableTransaction(
+            int contextHubId, long nanoAppId, IContextHubTransactionCallback onCompleteCallback) {
+        return new ContextHubServiceTransaction(
+                mNextAvailableId.getAndIncrement(), ContextHubTransaction.TYPE_DISABLE_NANOAPP) {
+            @Override
+            /* package */ int onTransact() {
+                try {
+                    return mContextHubProxy.disableNanoApp(
+                            contextHubId, nanoAppId, this.getTransactionId());
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException while trying to disable nanoapp with ID 0x" +
+                            Long.toHexString(nanoAppId), e);
+                    return Result.UNKNOWN_FAILURE;
+                }
+            }
+
+            @Override
+            /* package */ void onTransactionComplete(@ContextHubTransaction.Result int result) {
+                try {
+                    onCompleteCallback.onTransactionComplete(result);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "RemoteException while calling client onTransactionComplete", e);
+                }
+            }
+        };
+    }
+
+    /**
      * Creates a transaction for querying for a list of nanoapps.
      *
      * @param contextHubId       the ID of the hub to query
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index b324002..3bd6446 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -1328,6 +1328,14 @@
             } else if (!mStarted) {
                 // start GPS
                 startNavigating(singleShot);
+            } else {
+                // GNSS Engine is already ON, but no GPS_CAPABILITY_SCHEDULING
+                mAlarmManager.cancel(mTimeoutIntent);
+                if (mFixInterval >= NO_FIX_TIMEOUT) {
+                    // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
+                    // and our fix interval is not short
+                    mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                            SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent);                }
             }
         } else {
             updateClientUids(new WorkSource());
diff --git a/services/core/java/com/android/server/location/NanoAppStateManager.java b/services/core/java/com/android/server/location/NanoAppStateManager.java
index 81c4784..9869626 100644
--- a/services/core/java/com/android/server/location/NanoAppStateManager.java
+++ b/services/core/java/com/android/server/location/NanoAppStateManager.java
@@ -24,6 +24,7 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Iterator;
 import java.util.List;
 
 /**
@@ -153,11 +154,12 @@
             nanoAppIdSet.add(appInfo.appId);
         }
 
-        for (int nanoAppHandle : mNanoAppHash.keySet()) {
-            NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppHandle);
+        Iterator<NanoAppInstanceInfo> iterator = mNanoAppHash.values().iterator();
+        while (iterator.hasNext()) {
+            NanoAppInstanceInfo info = iterator.next();
             if (info.getContexthubId() == contextHubId &&
                     !nanoAppIdSet.contains(info.getAppId())) {
-                mNanoAppHash.remove(nanoAppHandle);
+                iterator.remove();
             }
         }
     }
diff --git a/services/core/java/com/android/server/location/OWNERS b/services/core/java/com/android/server/location/OWNERS
index d3c0ea8..5f0369b 100644
--- a/services/core/java/com/android/server/location/OWNERS
+++ b/services/core/java/com/android/server/location/OWNERS
@@ -1,4 +1,6 @@
 ashutoshj@google.com
 bduddie@google.com
+gomo@google.com
+sooniln@google.com
 weiwa@google.com
 wyattriley@google.com
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
new file mode 100644
index 0000000..e4d2b953
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/KeySyncUtils.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings.recoverablekeystore;
+
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
+/**
+ * Utility functions for the flow where the RecoverableKeyStoreLoader syncs keys with remote
+ * storage.
+ *
+ * @hide
+ */
+public class KeySyncUtils {
+
+    private static final String RECOVERY_KEY_ALGORITHM = "AES";
+    private static final int RECOVERY_KEY_SIZE_BITS = 256;
+
+    private static final byte[] THM_ENCRYPTED_RECOVERY_KEY_HEADER =
+            "V1 THM_encrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
+    private static final byte[] LOCALLY_ENCRYPTED_RECOVERY_KEY_HEADER =
+            "V1 locally_encrypted_recovery_key".getBytes(StandardCharsets.UTF_8);
+    private static final byte[] ENCRYPTED_APPLICATION_KEY_HEADER =
+            "V1 encrypted_application_key".getBytes(StandardCharsets.UTF_8);
+
+    private static final byte[] THM_KF_HASH_PREFIX = "THM_KF_hash".getBytes(StandardCharsets.UTF_8);
+
+    /**
+     * Encrypts the recovery key using both the lock screen hash and the remote storage's public
+     * key.
+     *
+     * @param publicKey The public key of the remote storage.
+     * @param lockScreenHash The user's lock screen hash.
+     * @param vaultParams Additional parameters to send to the remote storage.
+     * @param recoveryKey The recovery key.
+     * @return The encrypted bytes.
+     * @throws NoSuchAlgorithmException if any SecureBox algorithm is unavailable.
+     * @throws InvalidKeyException if the public key or the lock screen could not be used to encrypt
+     *     the data.
+     *
+     * @hide
+     */
+    public byte[] thmEncryptRecoveryKey(
+            PublicKey publicKey,
+            byte[] lockScreenHash,
+            byte[] vaultParams,
+            SecretKey recoveryKey
+    ) throws NoSuchAlgorithmException, InvalidKeyException {
+        byte[] encryptedRecoveryKey = locallyEncryptRecoveryKey(lockScreenHash, recoveryKey);
+        byte[] thmKfHash = calculateThmKfHash(lockScreenHash);
+        byte[] header = concat(THM_ENCRYPTED_RECOVERY_KEY_HEADER, vaultParams);
+        return SecureBox.encrypt(
+                /*theirPublicKey=*/ publicKey,
+                /*sharedSecret=*/ thmKfHash,
+                /*header=*/ header,
+                /*payload=*/ encryptedRecoveryKey);
+    }
+
+    /**
+     * Calculates the THM_KF hash of the lock screen hash.
+     *
+     * @param lockScreenHash The lock screen hash.
+     * @return The hash.
+     * @throws NoSuchAlgorithmException if SHA-256 is unavailable (should never happen).
+     *
+     * @hide
+     */
+    public static byte[] calculateThmKfHash(byte[] lockScreenHash)
+            throws NoSuchAlgorithmException {
+        MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
+        messageDigest.update(THM_KF_HASH_PREFIX);
+        messageDigest.update(lockScreenHash);
+        return messageDigest.digest();
+    }
+
+    /**
+     * Encrypts the recovery key using the lock screen hash.
+     *
+     * @param lockScreenHash The raw lock screen hash.
+     * @param recoveryKey The recovery key.
+     * @return The encrypted bytes.
+     * @throws NoSuchAlgorithmException if any SecureBox algorithm is unavailable.
+     * @throws InvalidKeyException if the hash cannot be used to encrypt for some reason.
+     */
+    private static byte[] locallyEncryptRecoveryKey(byte[] lockScreenHash, SecretKey recoveryKey)
+            throws NoSuchAlgorithmException, InvalidKeyException {
+        return SecureBox.encrypt(
+                /*theirPublicKey=*/ null,
+                /*sharedSecret=*/ lockScreenHash,
+                /*header=*/ LOCALLY_ENCRYPTED_RECOVERY_KEY_HEADER,
+                /*payload=*/ recoveryKey.getEncoded());
+    }
+
+    /**
+     * Returns a new random 256-bit AES recovery key.
+     *
+     * @hide
+     */
+    public static SecretKey generateRecoveryKey() throws NoSuchAlgorithmException {
+        KeyGenerator keyGenerator = KeyGenerator.getInstance(RECOVERY_KEY_ALGORITHM);
+        keyGenerator.init(RECOVERY_KEY_SIZE_BITS, SecureRandom.getInstanceStrong());
+        return keyGenerator.generateKey();
+    }
+
+    /**
+     * Encrypts all of the given keys with the recovery key, using SecureBox.
+     *
+     * @param recoveryKey The recovery key.
+     * @param keys The keys, indexed by their aliases.
+     * @return The encrypted key material, indexed by aliases.
+     * @throws NoSuchAlgorithmException if any of the SecureBox algorithms are unavailable.
+     * @throws InvalidKeyException if the recovery key is not appropriate for encrypting the keys.
+     *
+     * @hide
+     */
+    public static Map<String, byte[]> encryptKeysWithRecoveryKey(
+            SecretKey recoveryKey, Map<String, SecretKey> keys)
+            throws NoSuchAlgorithmException, InvalidKeyException {
+        HashMap<String, byte[]> encryptedKeys = new HashMap<>();
+        for (String alias : keys.keySet()) {
+            SecretKey key = keys.get(alias);
+            byte[] encryptedKey = SecureBox.encrypt(
+                    /*theirPublicKey=*/ null,
+                    /*sharedSecret=*/ recoveryKey.getEncoded(),
+                    /*header=*/ ENCRYPTED_APPLICATION_KEY_HEADER,
+                    /*payload=*/ key.getEncoded());
+            encryptedKeys.put(alias, encryptedKey);
+        }
+        return encryptedKeys;
+    }
+
+    /**
+     * Returns a new array, the contents of which are the concatenation of {@code a} and {@code b}.
+     */
+    private static byte[] concat(byte[] a, byte[] b) {
+        byte[] result = new byte[a.length + b.length];
+        System.arraycopy(a, 0, result, 0, a.length);
+        System.arraycopy(b, 0, result, a.length, b.length);
+        return result;
+    }
+
+    // Statics only
+    private KeySyncUtils() {}
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorage.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorage.java
index cd85a97..6a189ef 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorage.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorage.java
@@ -68,4 +68,13 @@
     SecretKey loadFromAndroidKeyStore(String alias) throws KeyStoreException,
             NoSuchAlgorithmException,
             UnrecoverableEntryException;
+
+    /**
+     * Removes the entry with the given {@code alias} from AndroidKeyStore.
+     *
+     * @throws KeyStoreException if an error occurred deleting the key from AndroidKeyStore.
+     *
+     * @hide
+     */
+    void removeFromAndroidKeyStore(String alias) throws KeyStoreException;
 }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageImpl.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageImpl.java
index 4e0751b..d4dede1 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageImpl.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageImpl.java
@@ -16,44 +16,40 @@
 
 package com.android.server.locksettings.recoverablekeystore;
 
+import android.security.keystore.AndroidKeyStoreProvider;
 import android.security.keystore.KeyProtection;
 
 import java.io.IOException;
 import java.security.KeyStore;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
 import java.security.UnrecoverableEntryException;
-import java.security.cert.CertificateException;
 
 import javax.crypto.SecretKey;
 
 /**
- * Implementation of {@link RecoverableKeyStorage}.
+ * Implementation of {@link RecoverableKeyStorage} for a specific application.
  *
  * <p>Persists wrapped keys to disk, and loads raw keys into AndroidKeyStore.
  *
  * @hide
  */
 public class RecoverableKeyStorageImpl implements RecoverableKeyStorage {
-    private static final String ANDROID_KEY_STORE_PROVIDER = "AndroidKeyStore";
-
     private final KeyStore mKeyStore;
 
     /**
-     * A new instance.
+     * A new instance, storing recoverable keys for the given {@code userId}.
      *
      * @throws KeyStoreException if unable to load AndroidKeyStore.
+     * @throws NoSuchProviderException if AndroidKeyStore is not in this version of Android. Should
+     *     never occur.
      *
      * @hide
      */
-    public static RecoverableKeyStorageImpl newInstance() throws KeyStoreException {
-        KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER);
-        try {
-            keyStore.load(/*param=*/ null);
-        } catch (CertificateException | IOException | NoSuchAlgorithmException e) {
-            // Should never happen.
-            throw new KeyStoreException("Unable to load keystore.", e);
-        }
+    public static RecoverableKeyStorageImpl newInstance(int userId) throws KeyStoreException,
+            NoSuchProviderException {
+        KeyStore keyStore = AndroidKeyStoreProvider.getKeyStoreForUid(userId);
         return new RecoverableKeyStorageImpl(keyStore);
     }
 
@@ -75,8 +71,7 @@
     }
 
     /**
-     * Imports {@code key} into AndroidKeyStore, keyed by the application's uid and the
-     * {@code alias}.
+     * Imports {@code key} into the application's AndroidKeyStore, keyed by {@code alias}.
      *
      * @param alias The alias of the key.
      * @param key The key.
@@ -93,7 +88,7 @@
     }
 
     /**
-     * Loads a key handle from AndroidKeyStore.
+     * Loads a key handle from the application's AndroidKeyStore.
      *
      * @param alias Alias of the key to load.
      * @return The key handle.
@@ -104,7 +99,18 @@
     @Override
     public SecretKey loadFromAndroidKeyStore(String alias)
             throws KeyStoreException, NoSuchAlgorithmException, UnrecoverableEntryException {
-        return ((KeyStore.SecretKeyEntry) mKeyStore.getEntry(alias, /*protParam=*/ null))
-                .getSecretKey();
+        return ((SecretKey) mKeyStore.getKey(alias, /*password=*/ null));
+    }
+
+    /**
+     * Removes the entry with the given {@code alias} from the application's AndroidKeyStore.
+     *
+     * @throws KeyStoreException if an error occurred deleting the key from AndroidKeyStore.
+     *
+     * @hide
+     */
+    @Override
+    public void removeFromAndroidKeyStore(String alias) throws KeyStoreException {
+        mKeyStore.deleteEntry(alias);
     }
 }
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java
new file mode 100644
index 0000000..457fdc1
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/SecureBox.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings.recoverablekeystore;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PublicKey;
+
+/**
+ * TODO(b/69056040) Add implementation of SecureBox. This is a placeholder so KeySyncUtils compiles.
+ *
+ * @hide
+ */
+public class SecureBox {
+    /**
+     * TODO(b/69056040) Add implementation of encrypt.
+     *
+     * @hide
+     */
+    public static byte[] encrypt(
+            PublicKey theirPublicKey, byte[] sharedSecret, byte[] header, byte[] payload)
+            throws NoSuchAlgorithmException, InvalidKeyException {
+        throw new UnsupportedOperationException("Needs to be implemented.");
+    }
+}
diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
index c97c66e..9002292 100644
--- a/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
+++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/WrappedKey.java
@@ -16,14 +16,21 @@
 
 package com.android.server.locksettings.recoverablekeystore;
 
+import android.util.Log;
+
+import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.KeyStoreException;
 import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
 
 import javax.crypto.Cipher;
 import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.NoSuchPaddingException;
 import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
 
 /**
  * A {@link javax.crypto.SecretKey} wrapped with AES/GCM/NoPadding.
@@ -31,7 +38,11 @@
  * @hide
  */
 public class WrappedKey {
+    private static final String TAG = "WrappedKey";
+
     private static final String KEY_WRAP_CIPHER_ALGORITHM = "AES/GCM/NoPadding";
+    private static final String APPLICATION_KEY_ALGORITHM = "AES";
+    private static final int GCM_TAG_LENGTH_BITS = 128;
 
     private final byte[] mNonce;
     private final byte[] mKeyMaterial;
@@ -112,4 +123,54 @@
     public byte[] getKeyMaterial() {
         return mKeyMaterial;
     }
+
+    /**
+     * Unwraps the {@code wrappedKeys} with the {@code platformKey}.
+     *
+     * @return The unwrapped keys, indexed by alias.
+     * @throws NoSuchAlgorithmException if AES/GCM/NoPadding Cipher or AES key type is unavailable.
+     *
+     * @hide
+     */
+    public static Map<String, SecretKey> unwrapKeys(
+            SecretKey platformKey,
+            Map<String, WrappedKey> wrappedKeys)
+            throws NoSuchAlgorithmException, NoSuchPaddingException {
+        HashMap<String, SecretKey> unwrappedKeys = new HashMap<>();
+        Cipher cipher = Cipher.getInstance(KEY_WRAP_CIPHER_ALGORITHM);
+
+        for (String alias : wrappedKeys.keySet()) {
+            WrappedKey wrappedKey = wrappedKeys.get(alias);
+            try {
+                cipher.init(
+                        Cipher.UNWRAP_MODE,
+                        platformKey,
+                        new GCMParameterSpec(GCM_TAG_LENGTH_BITS, wrappedKey.getNonce()));
+            } catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
+                Log.e(TAG,
+                        String.format(
+                                Locale.US,
+                                "Could not init Cipher to unwrap recoverable key with alias '%s'",
+                                alias),
+                        e);
+                continue;
+            }
+            SecretKey key;
+            try {
+                key = (SecretKey) cipher.unwrap(
+                        wrappedKey.getKeyMaterial(), APPLICATION_KEY_ALGORITHM, Cipher.SECRET_KEY);
+            } catch (InvalidKeyException | NoSuchAlgorithmException e) {
+                Log.e(TAG,
+                        String.format(
+                                Locale.US,
+                                "Error unwrapping recoverable key with alias '%s'",
+                                alias),
+                        e);
+                continue;
+            }
+            unwrappedKeys.put(alias, key);
+        }
+
+        return unwrappedKeys;
+    }
 }
diff --git a/services/core/java/com/android/server/pm/DumpState.java b/services/core/java/com/android/server/pm/DumpState.java
index 7ebef83..f4ee0ce 100644
--- a/services/core/java/com/android/server/pm/DumpState.java
+++ b/services/core/java/com/android/server/pm/DumpState.java
@@ -40,6 +40,7 @@
     public static final int DUMP_COMPILER_STATS = 1 << 21;
     public static final int DUMP_CHANGES = 1 << 22;
     public static final int DUMP_VOLUMES = 1 << 23;
+    public static final int DUMP_SERVICE_PERMISSIONS = 1 << 24;
 
     public static final int OPTION_SHOW_FILTERS = 1 << 0;
 
@@ -92,4 +93,4 @@
     public void setSharedUser(SharedUserSetting user) {
         mSharedUser = user;
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index f106391..696d895 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -173,6 +173,7 @@
 import android.content.pm.PackageParser.PackageLite;
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.PackageParser.ParseFlags;
+import android.content.pm.PackageParser.ServiceIntentInfo;
 import android.content.pm.PackageStats;
 import android.content.pm.PackageUserState;
 import android.content.pm.ParceledListSlice;
@@ -650,18 +651,15 @@
     @GuardedBy("mInstallLock")
     final Installer mInstaller;
 
-    /** Directory where installed third-party apps stored */
-    final File mAppInstallDir;
-
-    /**
-     * Directory to which applications installed internally have their
-     * 32 bit native libraries copied.
-     */
-    private File mAppLib32InstallDir;
-
-    // Directory containing the private parts (e.g. code and non-resource assets) of forward-locked
-    // apps.
-    final File mDrmAppPrivateInstallDir;
+    /** Directory where installed applications are stored */
+    private static final File sAppInstallDir =
+            new File(Environment.getDataDirectory(), "app");
+    /** Directory where installed application's 32-bit native libraries are copied. */
+    private static final File sAppLib32InstallDir =
+            new File(Environment.getDataDirectory(), "app-lib");
+    /** Directory where code and non-resource assets of forward-locked applications are stored */
+    private static final File sDrmAppPrivateInstallDir =
+            new File(Environment.getDataDirectory(), "app-private");
 
     // ----------------------------------------------------------------
 
@@ -742,9 +740,6 @@
     @GuardedBy("mAvailableFeatures")
     final ArrayMap<String, FeatureInfo> mAvailableFeatures;
 
-    // If mac_permissions.xml was found for seinfo labeling.
-    boolean mFoundPolicyFile;
-
     private final InstantAppRegistry mInstantAppRegistry;
 
     @GuardedBy("mPackages")
@@ -2445,11 +2440,6 @@
             Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
             mInstantAppRegistry = new InstantAppRegistry(this);
 
-            File dataDir = Environment.getDataDirectory();
-            mAppInstallDir = new File(dataDir, "app");
-            mAppLib32InstallDir = new File(dataDir, "app-lib");
-            mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
-
             ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
             final int builtInLibCount = libConfig.size();
             for (int i = 0; i < builtInLibCount; i++) {
@@ -2459,7 +2449,7 @@
                         SharedLibraryInfo.TYPE_BUILTIN, PLATFORM_PACKAGE_NAME, 0);
             }
 
-            mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
+            SELinuxMMAC.readInstallPolicy();
 
             Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "read user settings");
             mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false));
@@ -2737,9 +2727,9 @@
             if (!mOnlyCore) {
                 EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                         SystemClock.uptimeMillis());
-                scanDirTracedLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
+                scanDirTracedLI(sAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);
 
-                scanDirTracedLI(mDrmAppPrivateInstallDir, mDefParseFlags
+                scanDirTracedLI(sDrmAppPrivateInstallDir, mDefParseFlags
                         | PackageParser.PARSE_FORWARD_LOCK,
                         scanFlags | SCAN_REQUIRE_KNOWN, 0);
 
@@ -2908,7 +2898,18 @@
                 // NOTE: We ignore potential failures here during a system scan (like
                 // the rest of the commands above) because there's precious little we
                 // can do about it. A settings error is reported, though.
-                adjustCpuAbisForSharedUserLPw(setting.packages, null /*scannedPackage*/);
+                final List<String> changedAbiCodePath =
+                        adjustCpuAbisForSharedUserLPw(setting.packages, null /*scannedPackage*/);
+                if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
+                    for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
+                        final String codePathString = changedAbiCodePath.get(i);
+                        try {
+                            mInstaller.rmdex(codePathString,
+                                    getDexCodeInstructionSet(getPreferredInstructionSet()));
+                        } catch (InstallerException ignored) {
+                        }
+                    }
+                }
             }
 
             // Now that we know all the packages we are keeping,
@@ -8134,6 +8135,7 @@
                 int errorCode = PackageManager.INSTALL_SUCCEEDED;
 
                 if (throwable == null) {
+                    // TODO(toddke): move lower in the scan chain
                     // Static shared libraries have synthetic package names
                     if (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {
                         renameStaticSharedLibraryPackage(parseResult.pkg);
@@ -8486,7 +8488,7 @@
             // Set CPU Abis to application info.
             if ((scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0) {
                 final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, updatedPs);
-                derivePackageAbi(pkg, cpuAbiOverride, false, mAppLib32InstallDir);
+                derivePackageAbi(pkg, cpuAbiOverride, false);
             } else {
                 pkg.applicationInfo.primaryCpuAbi = updatedPs.primaryCpuAbiString;
                 pkg.applicationInfo.secondaryCpuAbi = updatedPs.secondaryCpuAbiString;
@@ -8594,7 +8596,7 @@
         }
 
         // Note that we invoke the following method only if we are about to unpack an application
-        PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags
+        PackageParser.Package scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags
                 | SCAN_UPDATE_SIGNATURE, currentTime, user);
 
         /*
@@ -9615,12 +9617,12 @@
         final PackageParser.Package scannedPkg;
         try {
             // Scan the parent
-            scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags, currentTime, user);
+            scannedPkg = scanPackageNewLI(pkg, parseFlags, scanFlags, currentTime, user);
             // Scan the children
             final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
             for (int i = 0; i < childCount; i++) {
                 PackageParser.Package childPkg = pkg.childPackages.get(i);
-                scanPackageLI(childPkg, parseFlags,
+                scanPackageNewLI(childPkg, parseFlags,
                         scanFlags, currentTime, user);
             }
         } finally {
@@ -9634,23 +9636,655 @@
         return scannedPkg;
     }
 
-    private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
+    /** The result of a package scan. */
+    private static class ScanResult {
+        /** Whether or not the package scan was successful */
+        public final boolean success;
+        /**
+         * The final package settings. This may be the same object passed in
+         * the {@link ScanRequest}, but, with modified values.
+         */
+        @Nullable public final PackageSetting pkgSetting;
+        /** ABI code paths that have changed in the package scan */
+        @Nullable public final List<String> changedAbiCodePath;
+        public ScanResult(
+                boolean success,
+                @Nullable PackageSetting pkgSetting,
+                @Nullable List<String> changedAbiCodePath) {
+            this.success = success;
+            this.pkgSetting = pkgSetting;
+            this.changedAbiCodePath = changedAbiCodePath;
+        }
+    }
+
+    /** A package to be scanned */
+    private static class ScanRequest {
+        /** The parsed package */
+        @NonNull public final PackageParser.Package pkg;
+        /** Shared user settings, if the package has a shared user */
+        @Nullable public final SharedUserSetting sharedUserSetting;
+        /**
+         * Package settings of the currently installed version.
+         * <p><em>IMPORTANT:</em> The contents of this object may be modified
+         * during scan.
+         */
+        @Nullable public final PackageSetting pkgSetting;
+        /** A copy of the settings for the currently installed version */
+        @Nullable public final PackageSetting oldPkgSetting;
+        /** Package settings for the disabled version on the /system partition */
+        @Nullable public final PackageSetting disabledPkgSetting;
+        /** Package settings for the installed version under its original package name */
+        @Nullable public final PackageSetting originalPkgSetting;
+        /** The real package name of a renamed application */
+        @Nullable public final String realPkgName;
+        public final @ParseFlags int parseFlags;
+        public final @ScanFlags int scanFlags;
+        /** The user for which the package is being scanned */
+        @Nullable public final UserHandle user;
+        /** Whether or not the platform package is being scanned */
+        public final boolean isPlatformPackage;
+        public ScanRequest(
+                @NonNull PackageParser.Package pkg,
+                @Nullable SharedUserSetting sharedUserSetting,
+                @Nullable PackageSetting pkgSetting,
+                @Nullable PackageSetting disabledPkgSetting,
+                @Nullable PackageSetting originalPkgSetting,
+                @Nullable String realPkgName,
+                @ParseFlags int parseFlags,
+                @ScanFlags int scanFlags,
+                boolean isPlatformPackage,
+                @Nullable UserHandle user) {
+            this.pkg = pkg;
+            this.pkgSetting = pkgSetting;
+            this.sharedUserSetting = sharedUserSetting;
+            this.oldPkgSetting = pkgSetting == null ? null : new PackageSetting(pkgSetting);
+            this.disabledPkgSetting = disabledPkgSetting;
+            this.originalPkgSetting = originalPkgSetting;
+            this.realPkgName = realPkgName;
+            this.parseFlags = parseFlags;
+            this.scanFlags = scanFlags;
+            this.isPlatformPackage = isPlatformPackage;
+            this.user = user;
+        }
+    }
+
+    @GuardedBy("mInstallLock")
+    private PackageParser.Package scanPackageNewLI(@NonNull PackageParser.Package pkg,
             final @ParseFlags int parseFlags, final @ScanFlags int scanFlags, long currentTime,
             @Nullable UserHandle user) throws PackageManagerException {
-        boolean success = false;
-        try {
-            final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,
-                    currentTime, user);
-            success = true;
-            return res;
-        } finally {
-            if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
-                // DELETE_DATA_ON_FAILURES is only used by frozen paths
-                destroyAppDataLIF(pkg, UserHandle.USER_ALL,
-                        StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
-                destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
+
+        final String renamedPkgName = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
+        final String realPkgName = getRealPackageName(pkg, renamedPkgName);
+        if (realPkgName != null) {
+            ensurePackageRenamed(pkg, renamedPkgName);
+        }
+        final PackageSetting originalPkgSetting = getOriginalPackageLocked(pkg, renamedPkgName);
+        final PackageSetting pkgSetting = mSettings.getPackageLPr(pkg.packageName);
+        final PackageSetting disabledPkgSetting =
+                mSettings.getDisabledSystemPkgLPr(pkg.packageName);
+
+        if (mTransferedPackages.contains(pkg.packageName)) {
+            Slog.w(TAG, "Package " + pkg.packageName
+                    + " was transferred to another, but its .apk remains");
+        }
+
+        synchronized (mPackages) {
+            applyPolicy(pkg, parseFlags, scanFlags);
+            assertPackageIsValid(pkg, parseFlags, scanFlags);
+
+            SharedUserSetting sharedUserSetting = null;
+            if (pkg.mSharedUserId != null) {
+                // SIDE EFFECTS; may potentially allocate a new shared user
+                sharedUserSetting = mSettings.getSharedUserLPw(
+                        pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
+                if (DEBUG_PACKAGE_SCANNING) {
+                    if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+                        Log.d(TAG, "Shared UserID " + pkg.mSharedUserId
+                                + " (uid=" + sharedUserSetting.userId + "):"
+                                + " packages=" + sharedUserSetting.packages);
+                }
+            }
+
+            boolean scanSucceeded = false;
+            try {
+                final ScanRequest request = new ScanRequest(pkg, sharedUserSetting, pkgSetting,
+                        disabledPkgSetting, originalPkgSetting, realPkgName, parseFlags, scanFlags,
+                        (pkg == mPlatformPackage), user);
+                final ScanResult result = scanPackageOnlyLI(request, mFactoryTest, currentTime);
+                if (result.success) {
+                    commitScanResultsLocked(request, result);
+                }
+                scanSucceeded = true;
+            } finally {
+                  if (!scanSucceeded && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
+                      // DELETE_DATA_ON_FAILURES is only used by frozen paths
+                      destroyAppDataLIF(pkg, UserHandle.USER_ALL,
+                              StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
+                      destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
+                  }
             }
         }
+        return pkg;
+    }
+
+    /**
+     * Commits the package scan and modifies system state.
+     * <p><em>WARNING:</em> The method may throw an excpetion in the middle
+     * of committing the package, leaving the system in an inconsistent state.
+     * This needs to be fixed so, once we get to this point, no errors are
+     * possible and the system is not left in an inconsistent state.
+     */
+    @GuardedBy("mPackages")
+    private void commitScanResultsLocked(@NonNull ScanRequest request, @NonNull ScanResult result)
+            throws PackageManagerException {
+        final PackageParser.Package pkg = request.pkg;
+        final @ParseFlags int parseFlags = request.parseFlags;
+        final @ScanFlags int scanFlags = request.scanFlags;
+        final PackageSetting oldPkgSetting = request.oldPkgSetting;
+        final PackageSetting originalPkgSetting = request.originalPkgSetting;
+        final UserHandle user = request.user;
+        final String realPkgName = request.realPkgName;
+        final PackageSetting pkgSetting = result.pkgSetting;
+        final List<String> changedAbiCodePath = result.changedAbiCodePath;
+        final boolean newPkgSettingCreated = (result.pkgSetting != request.pkgSetting);
+
+        if (newPkgSettingCreated) {
+            if (originalPkgSetting != null) {
+                mSettings.addRenamedPackageLPw(pkg.packageName, originalPkgSetting.name);
+            }
+            // THROWS: when we can't allocate a user id. add call to check if there's
+            // enough space to ensure we won't throw; otherwise, don't modify state
+            mSettings.addUserToSettingLPw(pkgSetting);
+
+            if (originalPkgSetting != null && (scanFlags & SCAN_CHECK_ONLY) == 0) {
+                mTransferedPackages.add(originalPkgSetting.name);
+            }
+        }
+        // TODO(toddke): Consider a method specifically for modifying the Package object
+        // post scan; or, moving this stuff out of the Package object since it has nothing
+        // to do with the package on disk.
+        // We need to have this here because addUserToSettingLPw() is sometimes responsible
+        // for creating the application ID. If we did this earlier, we would be saving the
+        // correct ID.
+        pkg.applicationInfo.uid = pkgSetting.appId;
+
+        mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting);
+
+        if ((scanFlags & SCAN_CHECK_ONLY) == 0 && realPkgName != null) {
+            mTransferedPackages.add(pkg.packageName);
+        }
+
+        // THROWS: when requested libraries that can't be found. it only changes
+        // the state of the passed in pkg object, so, move to the top of the method
+        // and allow it to abort
+        if ((scanFlags & SCAN_BOOTING) == 0
+                && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+            // Check all shared libraries and map to their actual file path.
+            // We only do this here for apps not on a system dir, because those
+            // are the only ones that can fail an install due to this.  We
+            // will take care of the system apps by updating all of their
+            // library paths after the scan is done. Also during the initial
+            // scan don't update any libs as we do this wholesale after all
+            // apps are scanned to avoid dependency based scanning.
+            updateSharedLibrariesLPr(pkg, null);
+        }
+
+        // All versions of a static shared library are referenced with the same
+        // package name. Internally, we use a synthetic package name to allow
+        // multiple versions of the same shared library to be installed. So,
+        // we need to generate the synthetic package name of the latest shared
+        // library in order to compare signatures.
+        PackageSetting signatureCheckPs = pkgSetting;
+        if (pkg.applicationInfo.isStaticSharedLibrary()) {
+            SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);
+            if (libraryEntry != null) {
+                signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);
+            }
+        }
+
+        final KeySetManagerService ksms = mSettings.mKeySetManagerService;
+        if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
+            if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
+                // We just determined the app is signed correctly, so bring
+                // over the latest parsed certs.
+                pkgSetting.signatures.mSignatures = pkg.mSignatures;
+            } else {
+                if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+                    throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
+                            "Package " + pkg.packageName + " upgrade keys do not match the "
+                                    + "previously installed version");
+                } else {
+                    pkgSetting.signatures.mSignatures = pkg.mSignatures;
+                    String msg = "System package " + pkg.packageName
+                            + " signature changed; retaining data.";
+                    reportSettingsProblem(Log.WARN, msg);
+                }
+            }
+        } else {
+            try {
+                final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg);
+                final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg);
+                final boolean compatMatch = verifySignatures(signatureCheckPs, pkg.mSignatures,
+                        compareCompat, compareRecover);
+                // The new KeySets will be re-added later in the scanning process.
+                if (compatMatch) {
+                    synchronized (mPackages) {
+                        ksms.removeAppKeySetDataLPw(pkg.packageName);
+                    }
+                }
+                // We just determined the app is signed correctly, so bring
+                // over the latest parsed certs.
+                pkgSetting.signatures.mSignatures = pkg.mSignatures;
+            } catch (PackageManagerException e) {
+                if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+                    throw e;
+                }
+                // The signature has changed, but this package is in the system
+                // image...  let's recover!
+                pkgSetting.signatures.mSignatures = pkg.mSignatures;
+                // However...  if this package is part of a shared user, but it
+                // doesn't match the signature of the shared user, let's fail.
+                // What this means is that you can't change the signatures
+                // associated with an overall shared user, which doesn't seem all
+                // that unreasonable.
+                if (signatureCheckPs.sharedUser != null) {
+                    if (compareSignatures(signatureCheckPs.sharedUser.signatures.mSignatures,
+                            pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
+                        throw new PackageManagerException(
+                                INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
+                                "Signature mismatch for shared user: "
+                                        + pkgSetting.sharedUser);
+                    }
+                }
+                // File a report about this.
+                String msg = "System package " + pkg.packageName
+                        + " signature changed; retaining data.";
+                reportSettingsProblem(Log.WARN, msg);
+            }
+        }
+
+        if ((scanFlags & SCAN_CHECK_ONLY) == 0 && pkg.mAdoptPermissions != null) {
+            // This package wants to adopt ownership of permissions from
+            // another package.
+            for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) {
+                final String origName = pkg.mAdoptPermissions.get(i);
+                final PackageSetting orig = mSettings.getPackageLPr(origName);
+                if (orig != null) {
+                    if (verifyPackageUpdateLPr(orig, pkg)) {
+                        Slog.i(TAG, "Adopting permissions from " + origName + " to "
+                                + pkg.packageName);
+                        mSettings.mPermissions.transferPermissions(origName, pkg.packageName);
+                    }
+                }
+            }
+        }
+
+        if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
+            for (int i = changedAbiCodePath.size() - 1; i <= 0; --i) {
+                final String codePathString = changedAbiCodePath.get(i);
+                try {
+                    mInstaller.rmdex(codePathString,
+                            getDexCodeInstructionSet(getPreferredInstructionSet()));
+                } catch (InstallerException ignored) {
+                }
+            }
+        }
+
+        if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
+            if (oldPkgSetting != null) {
+                synchronized (mPackages) {
+                    mSettings.mPackages.put(oldPkgSetting.name, oldPkgSetting);
+                }
+            }
+        } else {
+            final int userId = user == null ? 0 : user.getIdentifier();
+            // Modify state for the given package setting
+            commitPackageSettings(pkg, pkgSetting, user, scanFlags,
+                    (parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/);
+            if (pkgSetting.getInstantApp(userId)) {
+                mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);
+            }
+        }
+    }
+
+    /**
+     * Returns the "real" name of the package.
+     * <p>This may differ from the package's actual name if the application has already
+     * been installed under one of this package's original names.
+     */
+    private static @Nullable String getRealPackageName(@NonNull PackageParser.Package pkg,
+            @Nullable String renamedPkgName) {
+        if (pkg.mOriginalPackages == null || !pkg.mOriginalPackages.contains(renamedPkgName)) {
+            return null;
+        }
+        return pkg.mRealPackage;
+    }
+
+    /**
+     * Returns the original package setting.
+     * <p>A package can migrate its name during an update. In this scenario, a package
+     * designates a set of names that it considers as one of its original names.
+     * <p>An original package must be signed identically and it must have the same
+     * shared user [if any].
+     */
+    @GuardedBy("mPackages")
+    private @Nullable PackageSetting getOriginalPackageLocked(@NonNull PackageParser.Package pkg,
+            @Nullable String renamedPkgName) {
+        if (pkg.mOriginalPackages == null || pkg.mOriginalPackages.contains(renamedPkgName)) {
+            return null;
+        }
+        for (int i = pkg.mOriginalPackages.size() - 1; i >= 0; --i) {
+            final PackageSetting originalPs =
+                    mSettings.getPackageLPr(pkg.mOriginalPackages.get(i));
+            if (originalPs != null) {
+                // the package is already installed under its original name...
+                // but, should we use it?
+                if (!verifyPackageUpdateLPr(originalPs, pkg)) {
+                    // the new package is incompatible with the original
+                    continue;
+                } else if (originalPs.sharedUser != null) {
+                    if (!originalPs.sharedUser.name.equals(pkg.mSharedUserId)) {
+                        // the shared user id is incompatible with the original
+                        Slog.w(TAG, "Unable to migrate data from " + originalPs.name
+                                + " to " + pkg.packageName + ": old uid "
+                                + originalPs.sharedUser.name
+                                + " differs from " + pkg.mSharedUserId);
+                        continue;
+                    }
+                    // TODO: Add case when shared user id is added [b/28144775]
+                } else {
+                    if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "
+                            + pkg.packageName + " to old name " + originalPs.name);
+                }
+                return originalPs;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Renames the package if it was installed under a different name.
+     * <p>When we've already installed the package under an original name, update
+     * the new package so we can continue to have the old name.
+     */
+    private static void ensurePackageRenamed(@NonNull PackageParser.Package pkg,
+            @NonNull String renamedPackageName) {
+        if (pkg.mOriginalPackages == null
+                || !pkg.mOriginalPackages.contains(renamedPackageName)
+                || pkg.packageName.equals(renamedPackageName)) {
+            return;
+        }
+        pkg.setPackageName(renamedPackageName);
+    }
+
+    /**
+     * Just scans the package without any side effects.
+     * <p>Not entirely true at the moment. There is still one side effect -- this
+     * method potentially modifies a live {@link PackageSetting} object representing
+     * the package being scanned. This will be resolved in the future.
+     *
+     * @param request Information about the package to be scanned
+     * @param isUnderFactoryTest Whether or not the device is under factory test
+     * @param currentTime The current time, in millis
+     * @return The results of the scan
+     */
+    @GuardedBy("mInstallLock")
+    private static @NonNull ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,
+            boolean isUnderFactoryTest, long currentTime)
+                    throws PackageManagerException {
+        final PackageParser.Package pkg = request.pkg;
+        PackageSetting pkgSetting = request.pkgSetting;
+        final PackageSetting disabledPkgSetting = request.disabledPkgSetting;
+        final PackageSetting originalPkgSetting = request.originalPkgSetting;
+        final @ParseFlags int parseFlags = request.parseFlags;
+        final @ScanFlags int scanFlags = request.scanFlags;
+        final String realPkgName = request.realPkgName;
+        final SharedUserSetting sharedUserSetting = request.sharedUserSetting;
+        final UserHandle user = request.user;
+        final boolean isPlatformPackage = request.isPlatformPackage;
+
+        List<String> changedAbiCodePath = null;
+
+        if (DEBUG_PACKAGE_SCANNING) {
+            if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+                Log.d(TAG, "Scanning package " + pkg.packageName);
+        }
+
+        if (Build.IS_DEBUGGABLE &&
+                pkg.isPrivileged() &&
+                !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) {
+            PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg);
+        }
+
+        // Initialize package source and resource directories
+        final File scanFile = new File(pkg.codePath);
+        final File destCodeFile = new File(pkg.applicationInfo.getCodePath());
+        final File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
+
+        // We keep references to the derived CPU Abis from settings in oder to reuse
+        // them in the case where we're not upgrading or booting for the first time.
+        String primaryCpuAbiFromSettings = null;
+        String secondaryCpuAbiFromSettings = null;
+        boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
+
+        if (!needToDeriveAbi) {
+            if (pkgSetting != null) {
+                primaryCpuAbiFromSettings = pkgSetting.primaryCpuAbiString;
+                secondaryCpuAbiFromSettings = pkgSetting.secondaryCpuAbiString;
+            } else {
+                // Re-scanning a system package after uninstalling updates; need to derive ABI
+                needToDeriveAbi = true;
+            }
+        }
+
+        if (pkgSetting != null && pkgSetting.sharedUser != sharedUserSetting) {
+            PackageManagerService.reportSettingsProblem(Log.WARN,
+                    "Package " + pkg.packageName + " shared user changed from "
+                            + (pkgSetting.sharedUser != null
+                            ? pkgSetting.sharedUser.name : "<nothing>")
+                            + " to "
+                            + (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")
+                            + "; replacing with new");
+            pkgSetting = null;
+        }
+
+        String[] usesStaticLibraries = null;
+        if (pkg.usesStaticLibraries != null) {
+            usesStaticLibraries = new String[pkg.usesStaticLibraries.size()];
+            pkg.usesStaticLibraries.toArray(usesStaticLibraries);
+        }
+
+        final boolean createNewPackage = (pkgSetting == null);
+        if (createNewPackage) {
+            final String parentPackageName = (pkg.parentPackage != null)
+                    ? pkg.parentPackage.packageName : null;
+            final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
+            final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
+            // REMOVE SharedUserSetting from method; update in a separate call
+            pkgSetting = Settings.createNewSetting(pkg.packageName, originalPkgSetting,
+                    disabledPkgSetting, realPkgName, sharedUserSetting, destCodeFile,
+                    destResourceFile, pkg.applicationInfo.nativeLibraryRootDir,
+                    pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi,
+                    pkg.mVersionCode, pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
+                    user, true /*allowInstall*/, instantApp, virtualPreload,
+                    parentPackageName, pkg.getChildPackageNames(),
+                    UserManagerService.getInstance(), usesStaticLibraries,
+                    pkg.usesStaticLibrariesVersions);
+        } else {
+            // REMOVE SharedUserSetting from method; update in a separate call.
+            //
+            // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
+            // secondaryCpuAbi are not known at this point so we always update them
+            // to null here, only to reset them at a later point.
+            Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,
+                    destCodeFile, pkg.applicationInfo.nativeLibraryDir,
+                    pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi,
+                    pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
+                    pkg.getChildPackageNames(), UserManagerService.getInstance(),
+                    usesStaticLibraries, pkg.usesStaticLibrariesVersions);
+        }
+        if (createNewPackage && originalPkgSetting != null) {
+            // If we are first transitioning from an original package,
+            // fix up the new package's name now.  We need to do this after
+            // looking up the package under its new name, so getPackageLP
+            // can take care of fiddling things correctly.
+            pkg.setPackageName(originalPkgSetting.name);
+
+            // File a report about this.
+            String msg = "New package " + pkgSetting.realName
+                    + " renamed to replace old package " + pkgSetting.name;
+            reportSettingsProblem(Log.WARN, msg);
+        }
+
+        if (disabledPkgSetting != null) {
+            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
+        }
+
+        SELinuxMMAC.assignSeInfoValue(pkg);
+
+        pkg.mExtras = pkgSetting;
+        pkg.applicationInfo.processName = fixProcessName(
+                pkg.applicationInfo.packageName,
+                pkg.applicationInfo.processName);
+
+        if (!isPlatformPackage) {
+            // Get all of our default paths setup
+            pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);
+        }
+
+        final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
+
+        if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
+            if (needToDeriveAbi) {
+                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
+                final boolean extractNativeLibs = !pkg.isLibrary();
+                derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs);
+                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+
+                // Some system apps still use directory structure for native libraries
+                // in which case we might end up not detecting abi solely based on apk
+                // structure. Try to detect abi based on directory structure.
+                if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&
+                        pkg.applicationInfo.primaryCpuAbi == null) {
+                    setBundledAppAbisAndRoots(pkg, pkgSetting);
+                    setNativeLibraryPaths(pkg, sAppLib32InstallDir);
+                }
+            } else {
+                // This is not a first boot or an upgrade, don't bother deriving the
+                // ABI during the scan. Instead, trust the value that was stored in the
+                // package setting.
+                pkg.applicationInfo.primaryCpuAbi = primaryCpuAbiFromSettings;
+                pkg.applicationInfo.secondaryCpuAbi = secondaryCpuAbiFromSettings;
+
+                setNativeLibraryPaths(pkg, sAppLib32InstallDir);
+
+                if (DEBUG_ABI_SELECTION) {
+                    Slog.i(TAG, "Using ABIS and native lib paths from settings : " +
+                            pkg.packageName + " " + pkg.applicationInfo.primaryCpuAbi + ", " +
+                            pkg.applicationInfo.secondaryCpuAbi);
+                }
+            }
+        } else {
+            if ((scanFlags & SCAN_MOVE) != 0) {
+                // We haven't run dex-opt for this move (since we've moved the compiled output too)
+                // but we already have this packages package info in the PackageSetting. We just
+                // use that and derive the native library path based on the new codepath.
+                pkg.applicationInfo.primaryCpuAbi = pkgSetting.primaryCpuAbiString;
+                pkg.applicationInfo.secondaryCpuAbi = pkgSetting.secondaryCpuAbiString;
+            }
+
+            // Set native library paths again. For moves, the path will be updated based on the
+            // ABIs we've determined above. For non-moves, the path will be updated based on the
+            // ABIs we determined during compilation, but the path will depend on the final
+            // package path (after the rename away from the stage path).
+            setNativeLibraryPaths(pkg, sAppLib32InstallDir);
+        }
+
+        // This is a special case for the "system" package, where the ABI is
+        // dictated by the zygote configuration (and init.rc). We should keep track
+        // of this ABI so that we can deal with "normal" applications that run under
+        // the same UID correctly.
+        if (isPlatformPackage) {
+            pkg.applicationInfo.primaryCpuAbi = VMRuntime.getRuntime().is64Bit() ?
+                    Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
+        }
+
+        // If there's a mismatch between the abi-override in the package setting
+        // and the abiOverride specified for the install. Warn about this because we
+        // would've already compiled the app without taking the package setting into
+        // account.
+        if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
+            if (cpuAbiOverride == null && pkgSetting.cpuAbiOverrideString != null) {
+                Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +
+                        " for package " + pkg.packageName);
+            }
+        }
+
+        pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
+        pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
+        pkgSetting.cpuAbiOverrideString = cpuAbiOverride;
+
+        // Copy the derived override back to the parsed package, so that we can
+        // update the package settings accordingly.
+        pkg.cpuAbiOverride = cpuAbiOverride;
+
+        if (DEBUG_ABI_SELECTION) {
+            Slog.d(TAG, "Resolved nativeLibraryRoot for " + pkg.applicationInfo.packageName
+                    + " to root=" + pkg.applicationInfo.nativeLibraryRootDir + ", isa="
+                    + pkg.applicationInfo.nativeLibraryRootRequiresIsa);
+        }
+
+        // Push the derived path down into PackageSettings so we know what to
+        // clean up at uninstall time.
+        pkgSetting.legacyNativeLibraryPathString = pkg.applicationInfo.nativeLibraryRootDir;
+
+        if (DEBUG_ABI_SELECTION) {
+            Log.d(TAG, "Abis for package[" + pkg.packageName + "] are" +
+                    " primary=" + pkg.applicationInfo.primaryCpuAbi +
+                    " secondary=" + pkg.applicationInfo.secondaryCpuAbi);
+        }
+
+        if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
+            // We don't do this here during boot because we can do it all
+            // at once after scanning all existing packages.
+            //
+            // We also do this *before* we perform dexopt on this package, so that
+            // we can avoid redundant dexopts, and also to make sure we've got the
+            // code and package path correct.
+            changedAbiCodePath =
+                    adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages, pkg);
+        }
+
+        if (isUnderFactoryTest && pkg.requestedPermissions.contains(
+                android.Manifest.permission.FACTORY_TEST)) {
+            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
+        }
+
+        if (isSystemApp(pkg)) {
+            pkgSetting.isOrphaned = true;
+        }
+
+        // Take care of first install / last update times.
+        final long scanFileTime = getLastModifiedTime(pkg);
+        if (currentTime != 0) {
+            if (pkgSetting.firstInstallTime == 0) {
+                pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
+            } else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
+                pkgSetting.lastUpdateTime = currentTime;
+            }
+        } else if (pkgSetting.firstInstallTime == 0) {
+            // We need *something*.  Take time time stamp of the file.
+            pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
+        } else if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
+            if (scanFileTime != pkgSetting.timeStamp) {
+                // A package on the system image has changed; consider this
+                // to be an update.
+                pkgSetting.lastUpdateTime = scanFileTime;
+            }
+        }
+        pkgSetting.setTimeStamp(scanFileTime);
+
+        return new ScanResult(true, pkgSetting, changedAbiCodePath);
     }
 
     /**
@@ -9701,490 +10335,6 @@
         }
     }
 
-    private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
-            final @ParseFlags int parseFlags, final @ScanFlags int scanFlags, long currentTime,
-            @Nullable UserHandle user)
-                    throws PackageManagerException {
-        if (DEBUG_PACKAGE_SCANNING) {
-            if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
-                Log.d(TAG, "Scanning package " + pkg.packageName);
-        }
-
-        applyPolicy(pkg, parseFlags, scanFlags);
-
-        assertPackageIsValid(pkg, parseFlags, scanFlags);
-
-        if (Build.IS_DEBUGGABLE &&
-                pkg.isPrivileged() &&
-                SystemProperties.getBoolean(PROPERTY_NAME_PM_DEXOPT_PRIV_APPS_OOB, false)) {
-            PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg);
-        }
-
-        // Initialize package source and resource directories
-        final File scanFile = new File(pkg.codePath);
-        final File destCodeFile = new File(pkg.applicationInfo.getCodePath());
-        final File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
-
-        SharedUserSetting suid = null;
-        PackageSetting pkgSetting = null;
-
-        // Getting the package setting may have a side-effect, so if we
-        // are only checking if scan would succeed, stash a copy of the
-        // old setting to restore at the end.
-        PackageSetting nonMutatedPs = null;
-
-        // We keep references to the derived CPU Abis from settings in oder to reuse
-        // them in the case where we're not upgrading or booting for the first time.
-        String primaryCpuAbiFromSettings = null;
-        String secondaryCpuAbiFromSettings = null;
-        boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
-
-        // writer
-        synchronized (mPackages) {
-            if (pkg.mSharedUserId != null) {
-                // SIDE EFFECTS; may potentially allocate a new shared user
-                suid = mSettings.getSharedUserLPw(
-                        pkg.mSharedUserId, 0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
-                if (DEBUG_PACKAGE_SCANNING) {
-                    if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
-                        Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + suid.userId
-                                + "): packages=" + suid.packages);
-                }
-            }
-
-            // Check if we are renaming from an original package name.
-            PackageSetting origPackage = null;
-            String realName = null;
-            if (pkg.mOriginalPackages != null) {
-                // This package may need to be renamed to a previously
-                // installed name.  Let's check on that...
-                final String renamed = mSettings.getRenamedPackageLPr(pkg.mRealPackage);
-                if (pkg.mOriginalPackages.contains(renamed)) {
-                    // This package had originally been installed as the
-                    // original name, and we have already taken care of
-                    // transitioning to the new one.  Just update the new
-                    // one to continue using the old name.
-                    realName = pkg.mRealPackage;
-                    if (!pkg.packageName.equals(renamed)) {
-                        // Callers into this function may have already taken
-                        // care of renaming the package; only do it here if
-                        // it is not already done.
-                        pkg.setPackageName(renamed);
-                    }
-                } else {
-                    for (int i=pkg.mOriginalPackages.size()-1; i>=0; i--) {
-                        if ((origPackage = mSettings.getPackageLPr(
-                                pkg.mOriginalPackages.get(i))) != null) {
-                            // We do have the package already installed under its
-                            // original name...  should we use it?
-                            if (!verifyPackageUpdateLPr(origPackage, pkg)) {
-                                // New package is not compatible with original.
-                                origPackage = null;
-                                continue;
-                            } else if (origPackage.sharedUser != null) {
-                                // Make sure uid is compatible between packages.
-                                if (!origPackage.sharedUser.name.equals(pkg.mSharedUserId)) {
-                                    Slog.w(TAG, "Unable to migrate data from " + origPackage.name
-                                            + " to " + pkg.packageName + ": old uid "
-                                            + origPackage.sharedUser.name
-                                            + " differs from " + pkg.mSharedUserId);
-                                    origPackage = null;
-                                    continue;
-                                }
-                                // TODO: Add case when shared user id is added [b/28144775]
-                            } else {
-                                if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "
-                                        + pkg.packageName + " to old name " + origPackage.name);
-                            }
-                            break;
-                        }
-                    }
-                }
-            }
-
-            if (mTransferedPackages.contains(pkg.packageName)) {
-                Slog.w(TAG, "Package " + pkg.packageName
-                        + " was transferred to another, but its .apk remains");
-            }
-
-            // See comments in nonMutatedPs declaration
-            if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
-                PackageSetting foundPs = mSettings.getPackageLPr(pkg.packageName);
-                if (foundPs != null) {
-                    nonMutatedPs = new PackageSetting(foundPs);
-                }
-            }
-
-            if (!needToDeriveAbi) {
-                PackageSetting foundPs = mSettings.getPackageLPr(pkg.packageName);
-                if (foundPs != null) {
-                    primaryCpuAbiFromSettings = foundPs.primaryCpuAbiString;
-                    secondaryCpuAbiFromSettings = foundPs.secondaryCpuAbiString;
-                } else {
-                    // when re-adding a system package failed after uninstalling updates.
-                    needToDeriveAbi = true;
-                }
-            }
-
-            pkgSetting = mSettings.getPackageLPr(pkg.packageName);
-            if (pkgSetting != null && pkgSetting.sharedUser != suid) {
-                PackageManagerService.reportSettingsProblem(Log.WARN,
-                        "Package " + pkg.packageName + " shared user changed from "
-                                + (pkgSetting.sharedUser != null
-                                        ? pkgSetting.sharedUser.name : "<nothing>")
-                                + " to "
-                                + (suid != null ? suid.name : "<nothing>")
-                                + "; replacing with new");
-                pkgSetting = null;
-            }
-            final PackageSetting oldPkgSetting =
-                    pkgSetting == null ? null : new PackageSetting(pkgSetting);
-            final PackageSetting disabledPkgSetting =
-                    mSettings.getDisabledSystemPkgLPr(pkg.packageName);
-
-            String[] usesStaticLibraries = null;
-            if (pkg.usesStaticLibraries != null) {
-                usesStaticLibraries = new String[pkg.usesStaticLibraries.size()];
-                pkg.usesStaticLibraries.toArray(usesStaticLibraries);
-            }
-
-            if (pkgSetting == null) {
-                final String parentPackageName = (pkg.parentPackage != null)
-                        ? pkg.parentPackage.packageName : null;
-                final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;
-                final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;
-                // REMOVE SharedUserSetting from method; update in a separate call
-                pkgSetting = Settings.createNewSetting(pkg.packageName, origPackage,
-                        disabledPkgSetting, realName, suid, destCodeFile, destResourceFile,
-                        pkg.applicationInfo.nativeLibraryRootDir, pkg.applicationInfo.primaryCpuAbi,
-                        pkg.applicationInfo.secondaryCpuAbi, pkg.getLongVersionCode(),
-                        pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags, user,
-                        true /*allowInstall*/, instantApp, virtualPreload,
-                        parentPackageName, pkg.getChildPackageNames(),
-                        UserManagerService.getInstance(), usesStaticLibraries,
-                        pkg.usesStaticLibrariesVersions);
-                // SIDE EFFECTS; updates system state; move elsewhere
-                if (origPackage != null) {
-                    mSettings.addRenamedPackageLPw(pkg.packageName, origPackage.name);
-                }
-                mSettings.addUserToSettingLPw(pkgSetting);
-            } else {
-                // REMOVE SharedUserSetting from method; update in a separate call.
-                //
-                // TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,
-                // secondaryCpuAbi are not known at this point so we always update them
-                // to null here, only to reset them at a later point.
-                Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, suid, destCodeFile,
-                        pkg.applicationInfo.nativeLibraryDir, pkg.applicationInfo.primaryCpuAbi,
-                        pkg.applicationInfo.secondaryCpuAbi, pkg.applicationInfo.flags,
-                        pkg.applicationInfo.privateFlags, pkg.getChildPackageNames(),
-                        UserManagerService.getInstance(), usesStaticLibraries,
-                        pkg.usesStaticLibrariesVersions);
-            }
-            // SIDE EFFECTS; persists system state to files on disk; move elsewhere
-            mSettings.writeUserRestrictionsLPw(pkgSetting, oldPkgSetting);
-
-            // SIDE EFFECTS; modifies system state; move elsewhere
-            if (pkgSetting.origPackage != null) {
-                // If we are first transitioning from an original package,
-                // fix up the new package's name now.  We need to do this after
-                // looking up the package under its new name, so getPackageLP
-                // can take care of fiddling things correctly.
-                pkg.setPackageName(origPackage.name);
-
-                // File a report about this.
-                String msg = "New package " + pkgSetting.realName
-                        + " renamed to replace old package " + pkgSetting.name;
-                reportSettingsProblem(Log.WARN, msg);
-
-                // Make a note of it.
-                if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
-                    mTransferedPackages.add(origPackage.name);
-                }
-
-                // No longer need to retain this.
-                pkgSetting.origPackage = null;
-            }
-
-            // SIDE EFFECTS; modifies system state; move elsewhere
-            if ((scanFlags & SCAN_CHECK_ONLY) == 0 && realName != null) {
-                // Make a note of it.
-                mTransferedPackages.add(pkg.packageName);
-            }
-
-            if (mSettings.isDisabledSystemPackageLPr(pkg.packageName)) {
-                pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
-            }
-
-            if ((scanFlags & SCAN_BOOTING) == 0
-                    && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
-                // Check all shared libraries and map to their actual file path.
-                // We only do this here for apps not on a system dir, because those
-                // are the only ones that can fail an install due to this.  We
-                // will take care of the system apps by updating all of their
-                // library paths after the scan is done. Also during the initial
-                // scan don't update any libs as we do this wholesale after all
-                // apps are scanned to avoid dependency based scanning.
-                updateSharedLibrariesLPr(pkg, null);
-            }
-
-            if (mFoundPolicyFile) {
-                SELinuxMMAC.assignSeInfoValue(pkg);
-            }
-            pkg.applicationInfo.uid = pkgSetting.appId;
-            pkg.mExtras = pkgSetting;
-
-
-            // Static shared libs have same package with different versions where
-            // we internally use a synthetic package name to allow multiple versions
-            // of the same package, therefore we need to compare signatures against
-            // the package setting for the latest library version.
-            PackageSetting signatureCheckPs = pkgSetting;
-            if (pkg.applicationInfo.isStaticSharedLibrary()) {
-                SharedLibraryEntry libraryEntry = getLatestSharedLibraVersionLPr(pkg);
-                if (libraryEntry != null) {
-                    signatureCheckPs = mSettings.getPackageLPr(libraryEntry.apk);
-                }
-            }
-
-            final KeySetManagerService ksms = mSettings.mKeySetManagerService;
-            if (ksms.shouldCheckUpgradeKeySetLocked(signatureCheckPs, scanFlags)) {
-                if (ksms.checkUpgradeKeySetLocked(signatureCheckPs, pkg)) {
-                    // We just determined the app is signed correctly, so bring
-                    // over the latest parsed certs.
-                    pkgSetting.signatures.mSignatures = pkg.mSignatures;
-                } else {
-                    if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
-                        throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
-                                "Package " + pkg.packageName + " upgrade keys do not match the "
-                                + "previously installed version");
-                    } else {
-                        pkgSetting.signatures.mSignatures = pkg.mSignatures;
-                        String msg = "System package " + pkg.packageName
-                                + " signature changed; retaining data.";
-                        reportSettingsProblem(Log.WARN, msg);
-                    }
-                }
-            } else {
-                try {
-                    final boolean compareCompat = isCompatSignatureUpdateNeeded(pkg);
-                    final boolean compareRecover = isRecoverSignatureUpdateNeeded(pkg);
-                    final boolean compatMatch = verifySignatures(signatureCheckPs, pkg.mSignatures,
-                            compareCompat, compareRecover);
-                    // The new KeySets will be re-added later in the scanning process.
-                    if (compatMatch) {
-                        synchronized (mPackages) {
-                            ksms.removeAppKeySetDataLPw(pkg.packageName);
-                        }
-                    }
-                    // We just determined the app is signed correctly, so bring
-                    // over the latest parsed certs.
-                    pkgSetting.signatures.mSignatures = pkg.mSignatures;
-                } catch (PackageManagerException e) {
-                    if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
-                        throw e;
-                    }
-                    // The signature has changed, but this package is in the system
-                    // image...  let's recover!
-                    pkgSetting.signatures.mSignatures = pkg.mSignatures;
-                    // However...  if this package is part of a shared user, but it
-                    // doesn't match the signature of the shared user, let's fail.
-                    // What this means is that you can't change the signatures
-                    // associated with an overall shared user, which doesn't seem all
-                    // that unreasonable.
-                    if (signatureCheckPs.sharedUser != null) {
-                        if (compareSignatures(signatureCheckPs.sharedUser.signatures.mSignatures,
-                                pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
-                            throw new PackageManagerException(
-                                    INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES,
-                                    "Signature mismatch for shared user: "
-                                            + pkgSetting.sharedUser);
-                        }
-                    }
-                    // File a report about this.
-                    String msg = "System package " + pkg.packageName
-                            + " signature changed; retaining data.";
-                    reportSettingsProblem(Log.WARN, msg);
-                }
-            }
-
-            if ((scanFlags & SCAN_CHECK_ONLY) == 0 && pkg.mAdoptPermissions != null) {
-                // This package wants to adopt ownership of permissions from
-                // another package.
-                for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) {
-                    final String origName = pkg.mAdoptPermissions.get(i);
-                    final PackageSetting orig = mSettings.getPackageLPr(origName);
-                    if (orig != null) {
-                        if (verifyPackageUpdateLPr(orig, pkg)) {
-                            Slog.i(TAG, "Adopting permissions from " + origName + " to "
-                                    + pkg.packageName);
-                            // SIDE EFFECTS; updates permissions system state; move elsewhere
-                            mSettings.mPermissions.transferPermissions(origName, pkg.packageName);
-                        }
-                    }
-                }
-            }
-        }
-
-        pkg.applicationInfo.processName = fixProcessName(
-                pkg.applicationInfo.packageName,
-                pkg.applicationInfo.processName);
-
-        if (pkg != mPlatformPackage) {
-            // Get all of our default paths setup
-            pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);
-        }
-
-        final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
-
-        if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
-            if (needToDeriveAbi) {
-                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");
-                final boolean extractNativeLibs = !pkg.isLibrary();
-                derivePackageAbi(pkg, cpuAbiOverride, extractNativeLibs, mAppLib32InstallDir);
-                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
-
-                // Some system apps still use directory structure for native libraries
-                // in which case we might end up not detecting abi solely based on apk
-                // structure. Try to detect abi based on directory structure.
-                if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&
-                        pkg.applicationInfo.primaryCpuAbi == null) {
-                    setBundledAppAbisAndRoots(pkg, pkgSetting);
-                    setNativeLibraryPaths(pkg, mAppLib32InstallDir);
-                }
-            } else {
-                // This is not a first boot or an upgrade, don't bother deriving the
-                // ABI during the scan. Instead, trust the value that was stored in the
-                // package setting.
-                pkg.applicationInfo.primaryCpuAbi = primaryCpuAbiFromSettings;
-                pkg.applicationInfo.secondaryCpuAbi = secondaryCpuAbiFromSettings;
-
-                setNativeLibraryPaths(pkg, mAppLib32InstallDir);
-
-                if (DEBUG_ABI_SELECTION) {
-                    Slog.i(TAG, "Using ABIS and native lib paths from settings : " +
-                        pkg.packageName + " " + pkg.applicationInfo.primaryCpuAbi + ", " +
-                        pkg.applicationInfo.secondaryCpuAbi);
-                }
-            }
-        } else {
-            if ((scanFlags & SCAN_MOVE) != 0) {
-                // We haven't run dex-opt for this move (since we've moved the compiled output too)
-                // but we already have this packages package info in the PackageSetting. We just
-                // use that and derive the native library path based on the new codepath.
-                pkg.applicationInfo.primaryCpuAbi = pkgSetting.primaryCpuAbiString;
-                pkg.applicationInfo.secondaryCpuAbi = pkgSetting.secondaryCpuAbiString;
-            }
-
-            // Set native library paths again. For moves, the path will be updated based on the
-            // ABIs we've determined above. For non-moves, the path will be updated based on the
-            // ABIs we determined during compilation, but the path will depend on the final
-            // package path (after the rename away from the stage path).
-            setNativeLibraryPaths(pkg, mAppLib32InstallDir);
-        }
-
-        // This is a special case for the "system" package, where the ABI is
-        // dictated by the zygote configuration (and init.rc). We should keep track
-        // of this ABI so that we can deal with "normal" applications that run under
-        // the same UID correctly.
-        if (mPlatformPackage == pkg) {
-            pkg.applicationInfo.primaryCpuAbi = VMRuntime.getRuntime().is64Bit() ?
-                    Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0];
-        }
-
-        // If there's a mismatch between the abi-override in the package setting
-        // and the abiOverride specified for the install. Warn about this because we
-        // would've already compiled the app without taking the package setting into
-        // account.
-        if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {
-            if (cpuAbiOverride == null && pkgSetting.cpuAbiOverrideString != null) {
-                Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +
-                        " for package " + pkg.packageName);
-            }
-        }
-
-        pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
-        pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
-        pkgSetting.cpuAbiOverrideString = cpuAbiOverride;
-
-        // Copy the derived override back to the parsed package, so that we can
-        // update the package settings accordingly.
-        pkg.cpuAbiOverride = cpuAbiOverride;
-
-        if (DEBUG_ABI_SELECTION) {
-            Slog.d(TAG, "Resolved nativeLibraryRoot for " + pkg.applicationInfo.packageName
-                    + " to root=" + pkg.applicationInfo.nativeLibraryRootDir + ", isa="
-                    + pkg.applicationInfo.nativeLibraryRootRequiresIsa);
-        }
-
-        // Push the derived path down into PackageSettings so we know what to
-        // clean up at uninstall time.
-        pkgSetting.legacyNativeLibraryPathString = pkg.applicationInfo.nativeLibraryRootDir;
-
-        if (DEBUG_ABI_SELECTION) {
-            Log.d(TAG, "Abis for package[" + pkg.packageName + "] are" +
-                    " primary=" + pkg.applicationInfo.primaryCpuAbi +
-                    " secondary=" + pkg.applicationInfo.secondaryCpuAbi);
-        }
-
-        // SIDE EFFECTS; removes DEX files from disk; move elsewhere
-        if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {
-            // We don't do this here during boot because we can do it all
-            // at once after scanning all existing packages.
-            //
-            // We also do this *before* we perform dexopt on this package, so that
-            // we can avoid redundant dexopts, and also to make sure we've got the
-            // code and package path correct.
-            adjustCpuAbisForSharedUserLPw(pkgSetting.sharedUser.packages, pkg);
-        }
-
-        if (mFactoryTest && pkg.requestedPermissions.contains(
-                android.Manifest.permission.FACTORY_TEST)) {
-            pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
-        }
-
-        if (isSystemApp(pkg)) {
-            pkgSetting.isOrphaned = true;
-        }
-
-        // Take care of first install / last update times.
-        final long scanFileTime = getLastModifiedTime(pkg);
-        if (currentTime != 0) {
-            if (pkgSetting.firstInstallTime == 0) {
-                pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
-            } else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {
-                pkgSetting.lastUpdateTime = currentTime;
-            }
-        } else if (pkgSetting.firstInstallTime == 0) {
-            // We need *something*.  Take time time stamp of the file.
-            pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
-        } else if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
-            if (scanFileTime != pkgSetting.timeStamp) {
-                // A package on the system image has changed; consider this
-                // to be an update.
-                pkgSetting.lastUpdateTime = scanFileTime;
-            }
-        }
-        pkgSetting.setTimeStamp(scanFileTime);
-
-        if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
-            if (nonMutatedPs != null) {
-                synchronized (mPackages) {
-                    mSettings.mPackages.put(nonMutatedPs.name, nonMutatedPs);
-                }
-            }
-        } else {
-            final int userId = user == null ? 0 : user.getIdentifier();
-            // Modify state for the given package setting
-            commitPackageSettings(pkg, pkgSetting, user, scanFlags,
-                    (parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/);
-            if (pkgSetting.getInstantApp(userId)) {
-                mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);
-            }
-        }
-        return pkg;
-    }
-
     /**
      * Applies policy to the parsed package based upon the given policy flags.
      * Ensures the package is in a good state.
@@ -10192,7 +10342,7 @@
      * Implementation detail: This method must NOT have any side effect. It would
      * ideally be static, but, it requires locks to read system state.
      */
-    private void applyPolicy(PackageParser.Package pkg, final @ParseFlags int parseFlags,
+    private static void applyPolicy(PackageParser.Package pkg, final @ParseFlags int parseFlags,
             final @ScanFlags int scanFlags) {
         if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
@@ -10581,8 +10731,7 @@
      * be available for query, resolution, etc...
      */
     private void commitPackageSettings(PackageParser.Package pkg, PackageSetting pkgSetting,
-            UserHandle user, final @ScanFlags int scanFlags, boolean chatty)
-                    throws PackageManagerException {
+            UserHandle user, final @ScanFlags int scanFlags, boolean chatty) {
         final String pkgName = pkg.packageName;
         if (mCustomResolverComponentName != null &&
                 mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) {
@@ -10935,11 +11084,11 @@
      * If {@code extractLibs} is true, native libraries are extracted from the app if required.
      */
     private static void derivePackageAbi(PackageParser.Package pkg, String cpuAbiOverride,
-            boolean extractLibs, File appLib32InstallDir)
+            boolean extractLibs)
                     throws PackageManagerException {
         // Give ourselves some initial paths; we'll come back for another
         // pass once we've determined ABI below.
-        setNativeLibraryPaths(pkg, appLib32InstallDir);
+        setNativeLibraryPaths(pkg, sAppLib32InstallDir);
 
         // We would never need to extract libs for forward-locked and external packages,
         // since the container service will do it for us. We shouldn't attempt to
@@ -11089,7 +11238,7 @@
 
         // Now that we've calculated the ABIs and determined if it's an internal app,
         // we will go ahead and populate the nativeLibraryPath.
-        setNativeLibraryPaths(pkg, appLib32InstallDir);
+        setNativeLibraryPaths(pkg, sAppLib32InstallDir);
     }
 
     /**
@@ -11105,8 +11254,9 @@
      * NOTE: We currently only match for the primary CPU abi string. Matching the secondary
      * adds unnecessary complexity.
      */
-    private void adjustCpuAbisForSharedUserLPw(Set<PackageSetting> packagesForUser,
-            PackageParser.Package scannedPackage) {
+    private static @Nullable List<String> adjustCpuAbisForSharedUserLPw(
+            Set<PackageSetting> packagesForUser, PackageParser.Package scannedPackage) {
+        List<String> changedAbiCodePath = null;
         String requiredInstructionSet = null;
         if (scannedPackage != null && scannedPackage.applicationInfo.primaryCpuAbi != null) {
             requiredInstructionSet = VMRuntime.getInstructionSet(
@@ -11176,15 +11326,15 @@
                                     + (scannedPackage != null ? scannedPackage : "null")
                                     + ")");
                         }
-                        try {
-                            mInstaller.rmdex(ps.codePathString,
-                                    getDexCodeInstructionSet(getPreferredInstructionSet()));
-                        } catch (InstallerException ignored) {
+                        if (changedAbiCodePath == null) {
+                            changedAbiCodePath = new ArrayList<>();
                         }
+                        changedAbiCodePath.add(ps.codePathString);
                     }
                 }
             }
         }
+        return changedAbiCodePath;
     }
 
     private void setUpCustomResolverActivity(PackageParser.Package pkg) {
@@ -12409,7 +12559,12 @@
                     out.print(' ');
                     filter.service.printComponentShortName(out);
                     out.print(" filter ");
-                    out.println(Integer.toHexString(System.identityHashCode(filter)));
+                    out.print(Integer.toHexString(System.identityHashCode(filter)));
+                    if (filter.service.info.permission != null) {
+                        out.print(" permission "); out.println(filter.service.info.permission);
+                    } else {
+                        out.println();
+                    }
         }
 
         @Override
@@ -16621,7 +16776,7 @@
                 String abiOverride = (TextUtils.isEmpty(pkg.cpuAbiOverride) ?
                     args.abiOverride : pkg.cpuAbiOverride);
                 final boolean extractNativeLibs = !pkg.isLibrary();
-                derivePackageAbi(pkg, abiOverride, extractNativeLibs, mAppLib32InstallDir);
+                derivePackageAbi(pkg, abiOverride, extractNativeLibs);
             } catch (PackageManagerException pme) {
                 Slog.e(TAG, "Error deriving application ABI", pme);
                 res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
@@ -16947,7 +17102,7 @@
                 return name.startsWith("vmdl") && name.endsWith(".tmp");
             }
         };
-        for (File file : mDrmAppPrivateInstallDir.listFiles(filter)) {
+        for (File file : sDrmAppPrivateInstallDir.listFiles(filter)) {
             file.delete();
         }
     }
@@ -20402,6 +20557,7 @@
                 pw.println("    dexopt: dump dexopt state");
                 pw.println("    compiler-stats: dump compiler statistics");
                 pw.println("    enabled-overlays: dump list of enabled overlay packages");
+                pw.println("    service-permissions: dump permissions required by services");
                 pw.println("    <package.name>: info about given package");
                 return;
             } else if ("--checkin".equals(opt)) {
@@ -20537,6 +20693,8 @@
                 dumpState.setDump(DumpState.DUMP_COMPILER_STATS);
             } else if ("changes".equals(cmd)) {
                 dumpState.setDump(DumpState.DUMP_CHANGES);
+            } else if ("service-permissions".equals(cmd)) {
+                dumpState.setDump(DumpState.DUMP_SERVICE_PERMISSIONS);
             } else if ("write".equals(cmd)) {
                 synchronized (mPackages) {
                     mSettings.writeLPr();
@@ -20917,6 +21075,25 @@
                 ipw.decreaseIndent();
             }
 
+            if (!checkin && dumpState.isDumping(DumpState.DUMP_SERVICE_PERMISSIONS)
+                    && packageName == null) {
+                if (dumpState.onTitlePrinted()) pw.println();
+                pw.println("Service permissions:");
+
+                final Iterator<ServiceIntentInfo> filterIterator = mServices.filterIterator();
+                while (filterIterator.hasNext()) {
+                    final ServiceIntentInfo info = filterIterator.next();
+                    final ServiceInfo serviceInfo = info.service.info;
+                    final String permission = serviceInfo.permission;
+                    if (permission != null) {
+                        pw.print("    ");
+                        pw.print(serviceInfo.getComponentName().flattenToShortString());
+                        pw.print(": ");
+                        pw.println(permission);
+                    }
+                }
+            }
+
             if (!checkin && dumpState.isDumping(DumpState.DUMP_DEXOPT)) {
                 if (dumpState.onTitlePrinted()) pw.println();
                 dumpDexoptStateLPr(pw, packageName);
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 9733624..809e16c 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -131,7 +131,6 @@
      * using the full set of code paths when the package's process is started.
      */
     Set<String> oldCodePaths;
-    PackageSettingBase origPackage;
 
     /** Package name of the app that installed this package */
     String installerPackageName;
@@ -263,7 +262,6 @@
         lastUpdateTime = orig.lastUpdateTime;
         legacyNativeLibraryPathString = orig.legacyNativeLibraryPathString;
         // Intentionally skip oldCodePaths; it's not relevant for copies
-        origPackage = orig.origPackage;
         parentPackageName = orig.parentPackageName;
         primaryCpuAbiString = orig.primaryCpuAbiString;
         resourcePath = orig.resourcePath;
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index f0ce3c9..fbf3d82 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -59,6 +59,8 @@
     // All policy stanzas read from mac_permissions.xml. This is also the lock
     // to synchronize access during policy load and access attempts.
     private static List<Policy> sPolicies = new ArrayList<>();
+    /** Whether or not the policy files have been read */
+    private static boolean sPolicyRead;
 
     /** Path to MAC permissions on system image */
     private static final File[] MAC_PERMISSIONS =
@@ -88,6 +90,12 @@
      *         were loaded successfully; no partial loading is possible.
      */
     public static boolean readInstallPolicy() {
+        synchronized (sPolicies) {
+            if (sPolicyRead) {
+                return true;
+            }
+        }
+
         // Temp structure to hold the rules while we parse the xml file
         List<Policy> policies = new ArrayList<>();
 
@@ -142,7 +150,9 @@
         }
 
         synchronized (sPolicies) {
-            sPolicies = policies;
+            sPolicies.clear();
+            sPolicies.addAll(policies);
+            sPolicyRead = true;
 
             if (DEBUG_POLICY_ORDER) {
                 for (Policy policy : sPolicies) {
@@ -280,6 +290,12 @@
      */
     public static void assignSeInfoValue(PackageParser.Package pkg) {
         synchronized (sPolicies) {
+            if (!sPolicyRead) {
+                if (DEBUG_POLICY) {
+                    Slog.d(TAG, "Policy not read");
+                }
+                return;
+            }
             for (Policy policy : sPolicies) {
                 String seInfo = policy.getMatchedSeInfo(pkg);
                 if (seInfo != null) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 4a5772f..648f847 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -687,7 +687,6 @@
                     (childPkgNames != null) ? new ArrayList<>(childPkgNames) : null;
             pkgSetting.codePath = codePath;
             pkgSetting.legacyNativeLibraryPathString = legacyNativeLibraryPath;
-            pkgSetting.origPackage = originalPkg;
             pkgSetting.parentPackageName = parentPkgName;
             pkgSetting.pkgFlags = pkgFlags;
             pkgSetting.pkgPrivateFlags = pkgPrivateFlags;
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 4b13404..4564988 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -113,7 +113,8 @@
             UserManager.DISALLOW_OEM_UNLOCK,
             UserManager.DISALLOW_UNMUTE_DEVICE,
             UserManager.DISALLOW_AUTOFILL,
-            UserManager.DISALLOW_USER_SWITCH
+            UserManager.DISALLOW_USER_SWITCH,
+            UserManager.DISALLOW_UNIFIED_PASSWORD,
     });
 
     /**
@@ -192,7 +193,7 @@
             UserManager.DISALLOW_BLUETOOTH_SHARING
     );
 
-    /*
+    /**
      * Special user restrictions that are always applied to all users no matter who sets them.
      */
     private static final Set<String> PROFILE_GLOBAL_RESTRICTIONS = Sets.newArraySet(
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7c75f77..9752a57 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2499,7 +2499,7 @@
 
         // check if user has enabled this operation. SecurityException will be thrown if this app
         // has not been allowed by the user
-        final int mode = mAppOpsManager.checkOpNoThrow(outAppOp[0], callingUid, attrs.packageName);
+        final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, attrs.packageName);
         switch (mode) {
             case AppOpsManager.MODE_ALLOWED:
             case AppOpsManager.MODE_IGNORED:
diff --git a/services/core/java/com/android/server/vr/VrManagerInternal.java b/services/core/java/com/android/server/vr/VrManagerInternal.java
index 7b1e12e..35b6ad3 100644
--- a/services/core/java/com/android/server/vr/VrManagerInternal.java
+++ b/services/core/java/com/android/server/vr/VrManagerInternal.java
@@ -59,13 +59,6 @@
             int userId, int processId, @NonNull ComponentName calling);
 
     /**
-     * Set whether the system has acquired a sleep token.
-     *
-     * @param isAsleep is {@code true} if the device is asleep, or {@code false} otherwise.
-     */
-    public abstract void onSleepStateChanged(boolean isAsleep);
-
-    /**
      * Set whether the display used for VR output is on.
      *
      * @param isScreenOn is {@code true} if the display is on and can receive commands,
@@ -74,13 +67,6 @@
     public abstract void onScreenStateChanged(boolean isScreenOn);
 
     /**
-     * Set whether the keyguard is currently active/showing.
-     *
-     * @param isShowing is {@code true} if the keyguard is active/showing.
-     */
-    public abstract void onKeyguardStateChanged(boolean isShowing);
-
-    /**
      * Return NO_ERROR if the given package is installed on the device and enabled as a
      * VrListenerService for the given current user, or a negative error code indicating a failure.
      *
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index 7d55b68..21c6889 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -19,6 +19,7 @@
 
 import android.Manifest;
 import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerInternal.ScreenObserver;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.app.INotificationManager;
@@ -105,7 +106,8 @@
  *
  * @hide
  */
-public class VrManagerService extends SystemService implements EnabledComponentChangeListener{
+public class VrManagerService extends SystemService
+        implements EnabledComponentChangeListener, ScreenObserver {
 
     public static final String TAG = "VrManagerService";
     static final boolean DBG = false;
@@ -237,15 +239,17 @@
         }
     }
 
-    private void setSleepState(boolean isAsleep) {
-        setSystemState(FLAG_AWAKE, !isAsleep);
-    }
-
     private void setScreenOn(boolean isScreenOn) {
         setSystemState(FLAG_SCREEN_ON, isScreenOn);
     }
 
-    private void setKeyguardShowing(boolean isShowing) {
+    @Override
+    public void onAwakeStateChanged(boolean isAwake) {
+        setSystemState(FLAG_AWAKE, isAwake);
+    }
+
+    @Override
+    public void onKeyguardStateChanged(boolean isShowing) {
         setSystemState(FLAG_KEYGUARD_UNLOCKED, !isShowing);
     }
 
@@ -706,21 +710,11 @@
         }
 
         @Override
-        public void onSleepStateChanged(boolean isAsleep) {
-            VrManagerService.this.setSleepState(isAsleep);
-        }
-
-        @Override
         public void onScreenStateChanged(boolean isScreenOn) {
             VrManagerService.this.setScreenOn(isScreenOn);
         }
 
         @Override
-        public void onKeyguardStateChanged(boolean isShowing) {
-            VrManagerService.this.setKeyguardShowing(isShowing);
-        }
-
-        @Override
         public boolean isCurrentVrListener(String packageName, int userId) {
             return VrManagerService.this.isCurrentVrListener(packageName, userId);
         }
@@ -773,6 +767,9 @@
     @Override
     public void onBootPhase(int phase) {
         if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+            LocalServices.getService(ActivityManagerInternal.class)
+                    .registerScreenObserver(this);
+
             mNotificationManager = INotificationManager.Stub.asInterface(
                     ServiceManager.getService(Context.NOTIFICATION_SERVICE));
             synchronized (mLock) {
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 95b139a..2bda80d 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -234,16 +234,7 @@
 
     private static void populateTransformationMatrixLocked(WindowState windowState,
             Matrix outMatrix) {
-        sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
-        sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
-        sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDtDy;
-        sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDsDy;
-        sTempFloats[Matrix.MTRANS_X] = windowState.mShownPosition.x;
-        sTempFloats[Matrix.MTRANS_Y] = windowState.mShownPosition.y;
-        sTempFloats[Matrix.MPERSP_0] = 0;
-        sTempFloats[Matrix.MPERSP_1] = 0;
-        sTempFloats[Matrix.MPERSP_2] = 1;
-        outMatrix.setValues(sTempFloats);
+        windowState.getTransformationMatrix(sTempFloats, outMatrix);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/RemoteEventTrace.java b/services/core/java/com/android/server/wm/RemoteEventTrace.java
index 9f65ba3..b214d35 100644
--- a/services/core/java/com/android/server/wm/RemoteEventTrace.java
+++ b/services/core/java/com/android/server/wm/RemoteEventTrace.java
@@ -20,6 +20,7 @@
 import java.io.FileOutputStream;
 import java.io.DataOutputStream;
 
+import android.os.StrictMode;
 import android.util.Slog;
 import android.os.Debug;
 
@@ -40,22 +41,28 @@
     }
 
     void openSurfaceTransaction() {
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
         try {
             mOut.writeUTF("OpenTransaction");
             writeSigil();
         } catch (Exception e) {
             logException(e);
             mService.disableSurfaceTrace();
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
         }
     }
 
     void closeSurfaceTransaction() {
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
         try {
             mOut.writeUTF("CloseTransaction");
             writeSigil();
         } catch (Exception e) {
             logException(e);
             mService.disableSurfaceTrace();
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
index a12c2c4..d2cbf88 100644
--- a/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
+++ b/services/core/java/com/android/server/wm/RemoteSurfaceTrace.java
@@ -20,6 +20,7 @@
 import android.graphics.Region;
 import android.os.IBinder;
 import android.os.Parcel;
+import android.os.StrictMode;
 import android.util.Slog;
 import android.view.SurfaceControl;
 
@@ -54,67 +55,122 @@
 
     @Override
     public void setAlpha(float alpha) {
-        writeFloatEvent("Alpha", alpha);
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+        try {
+            writeFloatEvent("Alpha", alpha);
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
         super.setAlpha(alpha);
     }
 
     @Override
     public void setLayer(int zorder) {
-        writeIntEvent("Layer", zorder);
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+        try {
+            writeIntEvent("Layer", zorder);
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
         super.setLayer(zorder);
     }
 
     @Override
     public void setPosition(float x, float y) {
-        writeFloatEvent("Position", x, y);
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+        try {
+            writeFloatEvent("Position", x, y);
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
         super.setPosition(x, y);
     }
 
     @Override
     public void setGeometryAppliesWithResize() {
-        writeEvent("GeometryAppliesWithResize");
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+        try {
+            writeEvent("GeometryAppliesWithResize");
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
         super.setGeometryAppliesWithResize();
     }
 
     @Override
     public void setSize(int w, int h) {
-        writeIntEvent("Size", w, h);
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+        try {
+            writeIntEvent("Size", w, h);
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
         super.setSize(w, h);
     }
 
     @Override
     public void setWindowCrop(Rect crop) {
-        writeRectEvent("Crop", crop);
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+        try {
+            writeRectEvent("Crop", crop);
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
         super.setWindowCrop(crop);
     }
 
     @Override
     public void setFinalCrop(Rect crop) {
-        writeRectEvent("FinalCrop", crop);
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+        try {
+            writeRectEvent("FinalCrop", crop);
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
         super.setFinalCrop(crop);
     }
 
     @Override
     public void setLayerStack(int layerStack) {
-        writeIntEvent("LayerStack", layerStack);
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+        try {
+            writeIntEvent("LayerStack", layerStack);
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
         super.setLayerStack(layerStack);
     }
 
     @Override
     public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) {
-        writeFloatEvent("Matrix", dsdx, dtdx, dsdy, dtdy);
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+        try {
+            writeFloatEvent("Matrix", dsdx, dtdx, dsdy, dtdy);
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
         super.setMatrix(dsdx, dtdx, dsdy, dtdy);
     }
 
     @Override
     public void hide() {
-        writeEvent("Hide");
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+        try {
+            writeEvent("Hide");
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
         super.hide();
     }
 
     @Override
     public void show() {
-        writeEvent("Show");
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
+        try {
+            writeEvent("Show");
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
+        }
         super.show();
     }
 
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index b08eb18..e653e7d 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -431,7 +431,7 @@
             if (w.mAppOp == OP_NONE) {
                 return;
             }
-            final int mode = mService.mAppOps.checkOpNoThrow(w.mAppOp, w.getOwningUid(),
+            final int mode = mService.mAppOps.noteOpNoThrow(w.mAppOp, w.getOwningUid(),
                     w.getOwningPackage());
             w.setAppOpVisibilityLw(mode == MODE_ALLOWED || mode == MODE_DEFAULT);
         }, false /* traverseTopToBottom */);
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index ca9f3a9..fa7ea2f 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -203,7 +203,7 @@
 
                     // Post back to WM to handle clean-ups. We still need the input
                     // event handler for the last finishInputEvent()!
-                    mService.mH.sendEmptyMessage(H.FINISH_TASK_POSITIONING);
+                    mService.mTaskPositioningController.finishTaskPositioning();
                 }
                 handled = true;
             } catch (Exception e) {
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index bb5706c..4dfe290 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -22,6 +22,8 @@
 import android.annotation.Nullable;
 import android.app.IActivityManager;
 import android.os.RemoteException;
+import android.os.Handler;
+import android.os.Looper;
 import android.util.Slog;
 import android.view.Display;
 import android.view.IWindow;
@@ -37,6 +39,7 @@
     private final InputManagerService mInputManager;
     private final InputMonitor mInputMonitor;
     private final IActivityManager mActivityManager;
+    private final Handler mHandler;
 
     @GuardedBy("WindowManagerSerivce.mWindowMap")
     private @Nullable TaskPositioner mTaskPositioner;
@@ -50,11 +53,12 @@
     }
 
     TaskPositioningController(WindowManagerService service, InputManagerService inputManager,
-            InputMonitor inputMonitor, IActivityManager activityManager) {
+            InputMonitor inputMonitor, IActivityManager activityManager, Looper looper) {
         mService = service;
         mInputMonitor = inputMonitor;
         mInputManager = inputManager;
         mActivityManager = activityManager;
+        mHandler = new Handler(looper);
     }
 
     boolean startMovingTask(IWindow window, float startX, float startY) {
@@ -75,24 +79,27 @@
     }
 
     void handleTapOutsideTask(DisplayContent displayContent, int x, int y) {
-        int taskId = -1;
-        synchronized (mService.mWindowMap) {
-            final Task task = displayContent.findTaskForResizePoint(x, y);
-            if (task != null) {
-                if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/,
-                        task.preserveOrientationOnResize(), x, y)) {
-                    return;
+        mHandler.post(() -> {
+            int taskId = -1;
+            synchronized (mService.mWindowMap) {
+                final Task task = displayContent.findTaskForResizePoint(x, y);
+                if (task != null) {
+                    if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/,
+                            task.preserveOrientationOnResize(), x, y)) {
+                        return;
+                    }
+                    taskId = task.mTaskId;
+                } else {
+                    taskId = displayContent.taskIdFromPoint(x, y);
                 }
-                taskId = task.mTaskId;
-            } else {
-                taskId = displayContent.taskIdFromPoint(x, y);
             }
-        }
-        if (taskId >= 0) {
-            try {
-                mActivityManager.setFocusedTask(taskId);
-            } catch(RemoteException e) {}
-        }
+            if (taskId >= 0) {
+                try {
+                    mActivityManager.setFocusedTask(taskId);
+                } catch (RemoteException e) {
+                }
+            }
+        });
     }
 
     private boolean startPositioningLocked(WindowState win, boolean resize,
@@ -145,15 +152,17 @@
         return true;
     }
 
-    void finishPositioning() {
-        if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning");
+    void finishTaskPositioning() {
+        mHandler.post(() -> {
+            if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning");
 
-        synchronized (mService.mWindowMap) {
-            if (mTaskPositioner != null) {
-                mTaskPositioner.unregister();
-                mTaskPositioner = null;
-                mInputMonitor.updateInputWindowsLw(true /*force*/);
+            synchronized (mService.mWindowMap) {
+                if (mTaskPositioner != null) {
+                    mTaskPositioner.unregister();
+                    mTaskPositioner = null;
+                    mInputMonitor.updateInputWindowsLw(true /*force*/);
+                }
             }
-        }
+        });
     }
 }
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index 84ad576..5abda27 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -62,8 +62,8 @@
 
                 synchronized (this) {
                     if (!mTouchExcludeRegion.contains(x, y)) {
-                        mService.mH.obtainMessage(H.TAP_OUTSIDE_TASK,
-                                x, y, mDisplayContent).sendToTarget();
+                        mService.mTaskPositioningController.handleTapOutsideTask(
+                                mDisplayContent, x, y);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a787543..9fc9f3c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1086,8 +1086,8 @@
         mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
 
-        mTaskPositioningController =
-                new TaskPositioningController(this, mInputManager, mInputMonitor, mActivityManager);
+        mTaskPositioningController = new TaskPositioningController(
+                this, mInputManager, mInputMonitor, mActivityManager, mH.getLooper());
         mDragDropController = new DragDropController(this, mH.getLooper());
 
         LocalServices.addService(WindowManagerInternal.class, new LocalService());
@@ -4609,7 +4609,6 @@
         public static final int DO_ANIMATION_CALLBACK = 26;
 
         public static final int CLIENT_FREEZE_TIMEOUT = 30;
-        public static final int TAP_OUTSIDE_TASK = 31;
         public static final int NOTIFY_ACTIVITY_DRAWN = 32;
 
         public static final int ALL_WINDOWS_DRAWN = 33;
@@ -4623,8 +4622,6 @@
         public static final int RESET_ANR_MESSAGE = 38;
         public static final int WALLPAPER_DRAW_PENDING_TIMEOUT = 39;
 
-        public static final int FINISH_TASK_POSITIONING = 40;
-
         public static final int UPDATE_DOCKED_STACK_DIVIDER = 41;
 
         public static final int WINDOW_REPLACEMENT_TIMEOUT = 46;
@@ -4901,17 +4898,6 @@
                     break;
                 }
 
-                case TAP_OUTSIDE_TASK: {
-                    mTaskPositioningController.handleTapOutsideTask(
-                            (DisplayContent)msg.obj, msg.arg1, msg.arg2);
-                }
-                break;
-
-                case FINISH_TASK_POSITIONING: {
-                    mTaskPositioningController.finishPositioning();
-                }
-                break;
-
                 case NOTIFY_ACTIVITY_DRAWN:
                     try {
                         mActivityManager.notifyActivityDrawn((IBinder) msg.obj);
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index 2358695..b9dc9db 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -18,9 +18,23 @@
 
 import static android.os.Build.IS_USER;
 
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
 import android.os.ShellCommand;
+import android.os.UserHandle;
+import android.util.DisplayMetrics;
+import android.view.Display;
+import android.view.IWindowManager;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.io.PrintWriter;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * ShellCommands for WindowManagerService.
@@ -29,33 +43,279 @@
  */
 public class WindowManagerShellCommand extends ShellCommand {
 
-    private final WindowManagerService mService;
+    // IPC interface to activity manager -- don't need to do additional security checks.
+    private final IWindowManager mInterface;
+
+    // Internal service impl -- must perform security checks before touching.
+    private final WindowManagerService mInternal;
 
     public WindowManagerShellCommand(WindowManagerService service) {
-        mService = service;
+        mInterface = service;
+        mInternal = service;
     }
 
     @Override
     public int onCommand(String cmd) {
-        switch (cmd) {
-            case "tracing":
-                return mService.mWindowTracing.onShellCommand(this, getNextArgRequired());
-            default:
-                return handleDefaultCommands(cmd);
+        if (cmd == null) {
+            return handleDefaultCommands(cmd);
         }
+        final PrintWriter pw = getOutPrintWriter();
+        try {
+            switch (cmd) {
+                case "size":
+                    return runDisplaySize(pw);
+                case "density":
+                    return runDisplayDensity(pw);
+                case "overscan":
+                    return runDisplayOverscan(pw);
+                case "scaling":
+                    return runDisplayScaling(pw);
+                case "screen-capture":
+                    return runSetScreenCapture(pw);
+                case "dismiss-keyguard":
+                    return runDismissKeyguard(pw);
+                case "surface-trace":
+                    return runSurfaceTrace(pw);
+                case "tracing":
+                    // XXX this should probably be changed to use openFileForSystem() to create
+                    // the output trace file, so the shell gets the correct semantics for where
+                    // trace files can be written.
+                    return mInternal.mWindowTracing.onShellCommand(this,
+                            getNextArgRequired());
+                default:
+                    return handleDefaultCommands(cmd);
+            }
+        } catch (RemoteException e) {
+            pw.println("Remote exception: " + e);
+        }
+        return -1;
+    }
+
+    private int runDisplaySize(PrintWriter pw) throws RemoteException {
+        String size = getNextArg();
+        int w, h;
+        if (size == null) {
+            Point initialSize = new Point();
+            Point baseSize = new Point();
+            try {
+                mInterface.getInitialDisplaySize(Display.DEFAULT_DISPLAY, initialSize);
+                mInterface.getBaseDisplaySize(Display.DEFAULT_DISPLAY, baseSize);
+                pw.println("Physical size: " + initialSize.x + "x" + initialSize.y);
+                if (!initialSize.equals(baseSize)) {
+                    pw.println("Override size: " + baseSize.x + "x" + baseSize.y);
+                }
+            } catch (RemoteException e) {
+            }
+            return 0;
+        } else if ("reset".equals(size)) {
+            w = h = -1;
+        } else {
+            int div = size.indexOf('x');
+            if (div <= 0 || div >= (size.length()-1)) {
+                getErrPrintWriter().println("Error: bad size " + size);
+                return -1;
+            }
+            String wstr = size.substring(0, div);
+            String hstr = size.substring(div+1);
+            try {
+                w = parseDimension(wstr);
+                h = parseDimension(hstr);
+            } catch (NumberFormatException e) {
+                getErrPrintWriter().println("Error: bad number " + e);
+                return -1;
+            }
+        }
+
+        if (w >= 0 && h >= 0) {
+            // TODO(multidisplay): For now Configuration only applies to main screen.
+            mInterface.setForcedDisplaySize(Display.DEFAULT_DISPLAY, w, h);
+        } else {
+            mInterface.clearForcedDisplaySize(Display.DEFAULT_DISPLAY);
+        }
+        return 0;
+    }
+
+    private int runDisplayDensity(PrintWriter pw) throws RemoteException {
+        String densityStr = getNextArg();
+        int density;
+        if (densityStr == null) {
+            try {
+                int initialDensity = mInterface.getInitialDisplayDensity(Display.DEFAULT_DISPLAY);
+                int baseDensity = mInterface.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
+                pw.println("Physical density: " + initialDensity);
+                if (initialDensity != baseDensity) {
+                    pw.println("Override density: " + baseDensity);
+                }
+            } catch (RemoteException e) {
+            }
+            return 0;
+        } else if ("reset".equals(densityStr)) {
+            density = -1;
+        } else {
+            try {
+                density = Integer.parseInt(densityStr);
+            } catch (NumberFormatException e) {
+                getErrPrintWriter().println("Error: bad number " + e);
+                return -1;
+            }
+            if (density < 72) {
+                getErrPrintWriter().println("Error: density must be >= 72");
+                return -1;
+            }
+        }
+
+        if (density > 0) {
+            // TODO(multidisplay): For now Configuration only applies to main screen.
+            mInterface.setForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY, density,
+                    UserHandle.USER_CURRENT);
+        } else {
+            mInterface.clearForcedDisplayDensityForUser(Display.DEFAULT_DISPLAY,
+                    UserHandle.USER_CURRENT);
+        }
+        return 0;
+    }
+
+    private int runDisplayOverscan(PrintWriter pw) throws RemoteException {
+        String overscanStr = getNextArgRequired();
+        Rect rect = new Rect();
+        if ("reset".equals(overscanStr)) {
+            rect.set(0, 0, 0, 0);
+        } else {
+            final Pattern FLATTENED_PATTERN = Pattern.compile(
+                    "(-?\\d+),(-?\\d+),(-?\\d+),(-?\\d+)");
+            Matcher matcher = FLATTENED_PATTERN.matcher(overscanStr);
+            if (!matcher.matches()) {
+                getErrPrintWriter().println("Error: bad rectangle arg: " + overscanStr);
+                return -1;
+            }
+            rect.left = Integer.parseInt(matcher.group(1));
+            rect.top = Integer.parseInt(matcher.group(2));
+            rect.right = Integer.parseInt(matcher.group(3));
+            rect.bottom = Integer.parseInt(matcher.group(4));
+        }
+
+        mInterface.setOverscan(Display.DEFAULT_DISPLAY, rect.left, rect.top, rect.right,
+                rect.bottom);
+        return 0;
+    }
+
+    private int runDisplayScaling(PrintWriter pw) throws RemoteException {
+        String scalingStr = getNextArgRequired();
+        if ("auto".equals(scalingStr)) {
+            mInterface.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 0);
+        } else if ("off".equals(scalingStr)) {
+            mInterface.setForcedDisplayScalingMode(Display.DEFAULT_DISPLAY, 1);
+        } else {
+            getErrPrintWriter().println("Error: scaling must be 'auto' or 'off'");
+            return -1;
+        }
+        return 0;
+    }
+
+    private int runSetScreenCapture(PrintWriter pw) throws RemoteException {
+        String userIdStr = getNextArg();
+        String enableStr = getNextArg();
+        int userId;
+        boolean disable;
+
+        try {
+            userId = Integer.parseInt(userIdStr);
+        } catch (NumberFormatException e) {
+            getErrPrintWriter().println("Error: bad number " + e);
+            return -1;
+        }
+
+        disable = !Boolean.parseBoolean(enableStr);
+        mInternal.setScreenCaptureDisabled(userId, disable);
+        return 0;
+    }
+
+    private int runDismissKeyguard(PrintWriter pw) throws RemoteException {
+        mInterface.dismissKeyguard(null /* callback */);
+        return 0;
+    }
+
+    private int runSurfaceTrace(PrintWriter pw) throws RemoteException {
+        final ParcelFileDescriptor pfd;
+        try {
+            pfd = ParcelFileDescriptor.dup(getOutFileDescriptor());
+        } catch (IOException e) {
+            getErrPrintWriter().println("Unable to dup output stream: " + e.getMessage());
+            return -1;
+        }
+        mInternal.enableSurfaceTrace(pfd);
+
+        // Read input until an explicit quit command is sent or the stream is closed (meaning
+        // the user killed the command).
+        try {
+            InputStream input = getRawInputStream();
+            InputStreamReader converter = new InputStreamReader(input);
+            BufferedReader in = new BufferedReader(converter);
+            String line;
+
+            while ((line = in.readLine()) != null) {
+                if (line.length() <= 0) {
+                    // no-op
+                } else if ("q".equals(line) || "quit".equals(line)) {
+                    break;
+                } else {
+                    pw.println("Invalid command: " + line);
+                }
+            }
+        } catch (IOException e) {
+            e.printStackTrace(pw);
+        } finally {
+            mInternal.disableSurfaceTrace();
+            try {
+                pfd.close();
+            } catch (IOException e) {
+            }
+        }
+
+        return 0;
+    }
+
+    private int parseDimension(String s) throws NumberFormatException {
+        if (s.endsWith("px")) {
+            return Integer.parseInt(s.substring(0, s.length() - 2));
+        }
+        if (s.endsWith("dp")) {
+            int density;
+            try {
+                density = mInterface.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
+            } catch (RemoteException e) {
+                density = DisplayMetrics.DENSITY_DEFAULT;
+            }
+            return Integer.parseInt(s.substring(0, s.length() - 2)) * density /
+                    DisplayMetrics.DENSITY_DEFAULT;
+        }
+        return Integer.parseInt(s);
     }
 
     @Override
     public void onHelp() {
         PrintWriter pw = getOutPrintWriter();
-        pw.println("Window Manager (window) commands:");
+        pw.println("Window manager (window) commands:");
         pw.println("  help");
-        pw.println("    Print this help text.");
-        pw.println();
-        if (!IS_USER){
+        pw.println("      Print this help text.");
+        pw.println("  size [reset|WxH|WdpxHdp]");
+        pw.println("    Return or override display size.");
+        pw.println("    width and height in pixels unless suffixed with 'dp'.");
+        pw.println("  density [reset|DENSITY]");
+        pw.println("    Return or override display density.");
+        pw.println("  overscan [reset|LEFT,TOP,RIGHT,BOTTOM]");
+        pw.println("    Set overscan area for display.");
+        pw.println("  scaling [off|auto]");
+        pw.println("    Set display scaling mode.");
+        pw.println("  screen-capture [userId] [true|false]");
+        pw.println("    Enable or disable screen capture.");
+        pw.println("  dismiss-keyguard");
+        pw.println("    Dismiss the keyguard, prompting user for auth if necessary.");
+        pw.println("  surface-trace");
+        pw.println("    Log surface commands to stdout in a binary format.");
+        if (!IS_USER) {
             pw.println("  tracing (start | stop)");
-            pw.println("    start or stop window tracing");
-            pw.println();
+            pw.println("    Start or stop window tracing.");
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6bebcf4..ddc1eac 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -118,7 +118,9 @@
 import static com.android.server.wm.proto.WindowStateProto.IDENTIFIER;
 import static com.android.server.wm.proto.WindowStateProto.PARENT_FRAME;
 import static com.android.server.wm.proto.WindowStateProto.STACK_ID;
+import static com.android.server.wm.proto.WindowStateProto.SHOWN_POSITION;
 import static com.android.server.wm.proto.WindowStateProto.SURFACE_INSETS;
+import static com.android.server.wm.proto.WindowStateProto.SURFACE_POSITION;
 import static com.android.server.wm.proto.WindowStateProto.WINDOW_CONTAINER;
 
 import android.annotation.CallSuper;
@@ -3112,6 +3114,8 @@
         mContentFrame.writeToProto(proto, CONTENT_FRAME);
         mContentInsets.writeToProto(proto, CONTENT_INSETS);
         mAttrs.surfaceInsets.writeToProto(proto, SURFACE_INSETS);
+        mSurfacePosition.writeToProto(proto, SURFACE_POSITION);
+        mShownPosition.writeToProto(proto, SHOWN_POSITION);
         mWinAnimator.writeToProto(proto, ANIMATOR);
         proto.write(ANIMATING_EXIT, mAnimatingExit);
         for (int i = 0; i < mChildren.size(); i++) {
@@ -4314,6 +4318,25 @@
         mWinAnimator.onAnimationFinished();
     }
 
+    /**
+     * Retrieves the current transformation matrix of the window, relative to the display.
+     *
+     * @param float9 A temporary array of 9 floats.
+     * @param outMatrix Matrix to fill in the transformation.
+     */
+    void getTransformationMatrix(float[] float9, Matrix outMatrix) {
+        float9[Matrix.MSCALE_X] = mWinAnimator.mDsDx;
+        float9[Matrix.MSKEW_Y] = mWinAnimator.mDtDx;
+        float9[Matrix.MSKEW_X] = mWinAnimator.mDtDy;
+        float9[Matrix.MSCALE_Y] = mWinAnimator.mDsDy;
+        float9[Matrix.MTRANS_X] = mSurfacePosition.x + mShownPosition.x;
+        float9[Matrix.MTRANS_Y] = mSurfacePosition.y + mShownPosition.y;
+        float9[Matrix.MPERSP_0] = 0;
+        float9[Matrix.MPERSP_1] = 0;
+        float9[Matrix.MPERSP_2] = 1;
+        outMatrix.setValues(float9);
+    }
+
     // TODO: Hack to work around the number of states AppWindowToken needs to access without having
     // access to its windows children. Need to investigate re-writing
     // {@link AppWindowToken#updateReportedVisibilityLocked} so this can be removed.
diff --git a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
index 222ac5b..9892146 100644
--- a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
+++ b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp
@@ -257,7 +257,7 @@
     { "nativeFinalize", "(J)V", (void*)nativeFinalize },
     { "nativeLoadModules", "(J)Ljava/util/List;", (void*)nativeLoadModules },
     { "nativeOpenTuner", "(JILandroid/hardware/radio/RadioManager$BandConfig;Z"
-            "Landroid/hardware/radio/ITunerCallback;)Lcom/android/server/broadcastradio/Tuner;",
+            "Landroid/hardware/radio/ITunerCallback;)Lcom/android/server/broadcastradio/hal1/Tuner;",
             (void*)nativeOpenTuner },
 };
 
@@ -270,7 +270,7 @@
 
     register_android_server_broadcastradio_convert(env);
 
-    auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/Tuner");
+    auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/Tuner");
     gjni.Tuner.clazz = MakeGlobalRefOrDie(env, tunerClass);
     gjni.Tuner.cstor = GetMethodIDOrDie(env, tunerClass, "<init>",
             "(Landroid/hardware/radio/ITunerCallback;IIZI)V");
@@ -281,7 +281,7 @@
     gjni.ArrayList.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
 
     auto res = jniRegisterNativeMethods(env,
-            "com/android/server/broadcastradio/BroadcastRadioService",
+            "com/android/server/broadcastradio/hal1/BroadcastRadioService",
             gRadioServiceMethods, NELEM(gRadioServiceMethods));
     LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
 }
diff --git a/services/core/jni/BroadcastRadio/Tuner.cpp b/services/core/jni/BroadcastRadio/Tuner.cpp
index 63339e9..42eb873 100644
--- a/services/core/jni/BroadcastRadio/Tuner.cpp
+++ b/services/core/jni/BroadcastRadio/Tuner.cpp
@@ -592,18 +592,18 @@
 
     register_android_server_broadcastradio_TunerCallback(vm, env);
 
-    auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/Tuner");
+    auto tunerClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/Tuner");
     gjni.Tuner.nativeContext = GetFieldIDOrDie(env, tunerClass, "mNativeContext", "J");
     gjni.Tuner.region = GetFieldIDOrDie(env, tunerClass, "mRegion", "I");
     gjni.Tuner.tunerCallback = GetFieldIDOrDie(env, tunerClass, "mTunerCallback",
-            "Lcom/android/server/broadcastradio/TunerCallback;");
+            "Lcom/android/server/broadcastradio/hal1/TunerCallback;");
 
     auto arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
     gjni.ArrayList.clazz = MakeGlobalRefOrDie(env, arrayListClass);
     gjni.ArrayList.cstor = GetMethodIDOrDie(env, arrayListClass, "<init>", "()V");
     gjni.ArrayList.add = GetMethodIDOrDie(env, arrayListClass, "add", "(Ljava/lang/Object;)Z");
 
-    auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/Tuner",
+    auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/hal1/Tuner",
             gTunerMethods, NELEM(gTunerMethods));
     LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
 }
diff --git a/services/core/jni/BroadcastRadio/TunerCallback.cpp b/services/core/jni/BroadcastRadio/TunerCallback.cpp
index d0ba005..39f2c05 100644
--- a/services/core/jni/BroadcastRadio/TunerCallback.cpp
+++ b/services/core/jni/BroadcastRadio/TunerCallback.cpp
@@ -406,7 +406,7 @@
 }
 
 static const JNINativeMethod gTunerCallbackMethods[] = {
-    { "nativeInit", "(Lcom/android/server/broadcastradio/Tuner;I)J", (void*)nativeInit },
+    { "nativeInit", "(Lcom/android/server/broadcastradio/hal1/Tuner;I)J", (void*)nativeInit },
     { "nativeFinalize", "(J)V", (void*)nativeFinalize },
     { "nativeDetach", "(J)V", (void*)nativeDetach },
 };
@@ -420,7 +420,7 @@
 
     gvm = vm;
 
-    auto tunerCbClass = FindClassOrDie(env, "com/android/server/broadcastradio/TunerCallback");
+    auto tunerCbClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/TunerCallback");
     gjni.TunerCallback.clazz = MakeGlobalRefOrDie(env, tunerCbClass);
     gjni.TunerCallback.nativeContext = GetFieldIDOrDie(env, tunerCbClass, "mNativeContext", "J");
     gjni.TunerCallback.handleHwFailure = GetMethodIDOrDie(env, tunerCbClass, "handleHwFailure", "()V");
@@ -444,7 +444,7 @@
     gjni.TunerCallback.onParametersUpdated = GetMethodIDOrDie(env, tunerCbClass,
             "onParametersUpdated", "(Ljava/util/Map;)V");
 
-    auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/TunerCallback",
+    auto res = jniRegisterNativeMethods(env, "com/android/server/broadcastradio/hal1/TunerCallback",
             gTunerCallbackMethods, NELEM(gTunerCallbackMethods));
     LOG_ALWAYS_FATAL_IF(res < 0, "Unable to register native methods.");
 }
diff --git a/services/core/jni/BroadcastRadio/convert.cpp b/services/core/jni/BroadcastRadio/convert.cpp
index f5381a8..be1ad72 100644
--- a/services/core/jni/BroadcastRadio/convert.cpp
+++ b/services/core/jni/BroadcastRadio/convert.cpp
@@ -678,7 +678,7 @@
     gjni.AmBandDescriptor.cstor = GetMethodIDOrDie(env, amBandDescriptorClass,
             "<init>", "(IIIIIZ)V");
 
-    auto convertClass = FindClassOrDie(env, "com/android/server/broadcastradio/Convert");
+    auto convertClass = FindClassOrDie(env, "com/android/server/broadcastradio/hal1/Convert");
     gjni.Convert.clazz = MakeGlobalRefOrDie(env, convertClass);
     gjni.Convert.stringMapToNative = GetStaticMethodIDOrDie(env, convertClass, "stringMapToNative",
             "(Ljava/util/Map;)[[Ljava/lang/String;");
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 9163037..5b9e3a1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -18,6 +18,8 @@
 import android.app.admin.IDevicePolicyManager;
 import android.content.ComponentName;
 import android.os.PersistableBundle;
+import android.security.keymaster.KeymasterCertificateChain;
+import android.security.keystore.ParcelableKeyGenParameterSpec;
 
 import com.android.internal.R;
 import com.android.server.SystemService;
@@ -56,8 +58,17 @@
      * @see {@link SystemService#onStopUser}
      */
     abstract void handleStopUser(int userId);
-    
+
     public void setSystemSetting(ComponentName who, String setting, String value){}
 
     public void transferOwner(ComponentName admin, ComponentName target, PersistableBundle bundle) {}
+
+    public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm,
+            ParcelableKeyGenParameterSpec keySpec, KeymasterCertificateChain attestationChain) {
+        return false;
+    }
+
+    public boolean isUsingUnifiedPassword(ComponentName who) {
+        return true;
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2080b72..bead31f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -150,13 +150,16 @@
 import android.provider.ContactsInternal;
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.security.Credentials;
 import android.security.IKeyChainAliasCallback;
 import android.security.IKeyChainService;
 import android.security.KeyChain;
 import android.security.KeyChain.KeyChainConnection;
+import android.security.keymaster.KeymasterCertificateChain;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.ParcelableKeyGenParameterSpec;
 import android.security.KeyStore;
+import android.security.keystore.AttestationUtils;
 import android.service.persistentdata.PersistentDataBlockManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -4079,6 +4082,17 @@
     }
 
     @Override
+    public boolean isUsingUnifiedPassword(ComponentName admin) {
+        if (!mHasFeature) {
+            return true;
+        }
+        final int userId = mInjector.userHandleGetCallingUserId();
+        enforceProfileOrDeviceOwner(admin);
+        enforceManagedProfile(userId, "query unified challenge status");
+        return !isSeparateProfileChallengeEnabled(userId);
+    }
+
+    @Override
     public boolean isProfileActivePasswordSufficientForParent(int userHandle) {
         if (!mHasFeature) {
             return true;
@@ -4913,19 +4927,22 @@
 
     @Override
     public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm,
-            ParcelableKeyGenParameterSpec parcelableKeySpec) {
+            ParcelableKeyGenParameterSpec parcelableKeySpec,
+            KeymasterCertificateChain attestationChain) {
         enforceCanManageScope(who, callerPackage, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER,
                 DELEGATION_CERT_INSTALL);
         final KeyGenParameterSpec keySpec = parcelableKeySpec.getSpec();
         if (TextUtils.isEmpty(keySpec.getKeystoreAlias())) {
             throw new IllegalArgumentException("Empty alias provided.");
         }
+        final String alias = keySpec.getKeystoreAlias();
         // As the caller will be granted access to the key, ensure no UID was specified, as
         // it will not have the desired effect.
         if (keySpec.getUid() != KeyStore.UID_SELF) {
             Log.e(LOG_TAG, "Only the caller can be granted access to the generated keypair.");
             return false;
         }
+
         final int callingUid = mInjector.binderGetCallingUid();
 
         final UserHandle userHandle = mInjector.binderGetCallingUserHandle();
@@ -4934,7 +4951,16 @@
             try (KeyChainConnection keyChainConnection =
                     KeyChain.bindAsUser(mContext, userHandle)) {
                 IKeyChainService keyChain = keyChainConnection.getService();
-                final boolean generationResult = keyChain.generateKeyPair(algorithm, parcelableKeySpec);
+
+                // Copy the provided keySpec, excluding the attestation challenge, which will be
+                // used later for requesting key attestation record.
+                final KeyGenParameterSpec noAttestationSpec =
+                    new KeyGenParameterSpec.Builder(keySpec)
+                        .setAttestationChallenge(null)
+                        .build();
+
+                final boolean generationResult = keyChain.generateKeyPair(algorithm,
+                    new ParcelableKeyGenParameterSpec(noAttestationSpec));
                 if (!generationResult) {
                     Log.e(LOG_TAG, "KeyChain failed to generate a keypair.");
                     return false;
@@ -4945,7 +4971,19 @@
                 // Note the use of the calling  UID, since the request for the private
                 // key will come from the client's process, so the grant has to be for
                 // that UID.
-                keyChain.setGrant(callingUid, keySpec.getKeystoreAlias(), true);
+                keyChain.setGrant(callingUid, alias, true);
+
+                final byte[] attestationChallenge = keySpec.getAttestationChallenge();
+                if (attestationChallenge != null) {
+                    final boolean attestationResult = keyChain.attestKey(
+                            alias, attestationChallenge, attestationChain);
+                    if (!attestationResult) {
+                        Log.e(LOG_TAG, String.format(
+                                "Attestation for %s failed, deleting key.", alias));
+                        keyChain.removeKeyPair(alias);
+                        return false;
+                    }
+                }
                 return true;
             }
         } catch (RemoteException e) {
diff --git a/services/robotests/src/com/android/server/backup/TransportManagerTest.java b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
index 2cb4a69..13623e5 100644
--- a/services/robotests/src/com/android/server/backup/TransportManagerTest.java
+++ b/services/robotests/src/com/android/server/backup/TransportManagerTest.java
@@ -18,9 +18,13 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static junit.framework.Assert.fail;
+
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 import static org.robolectric.shadow.api.Shadow.extract;
 
+import android.annotation.Nullable;
 import android.app.backup.BackupManager;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -29,15 +33,18 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 
-import com.android.server.backup.testing.BackupTransportStub;
+import com.android.internal.backup.IBackupTransport;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.server.backup.testing.ShadowBackupTransportStub;
 import com.android.server.backup.testing.ShadowContextImplForBackup;
 import com.android.server.backup.testing.ShadowPackageManagerForBackup;
 import com.android.server.backup.testing.TransportBoundListenerStub;
 import com.android.server.backup.testing.TransportReadyCallbackStub;
 import com.android.server.backup.transport.TransportClient;
+import com.android.server.backup.transport.TransportNotRegisteredException;
 import com.android.server.testing.FrameworkRobolectricTestRunner;
 import com.android.server.testing.SystemLoaderClasses;
 
@@ -95,15 +102,29 @@
                 (ShadowPackageManagerForBackup)
                         extract(RuntimeEnvironment.application.getPackageManager());
 
-        mTransport1 = new TransportInfo(PACKAGE_NAME, "transport1.name");
-        mTransport2 = new TransportInfo(PACKAGE_NAME, "transport2.name");
+        mTransport1 = new TransportInfo(
+                PACKAGE_NAME,
+                "transport1.name",
+                new Intent(),
+                "currentDestinationString",
+                new Intent(),
+                "dataManagementLabel");
+        mTransport2 = new TransportInfo(
+                PACKAGE_NAME,
+                "transport2.name",
+                new Intent(),
+                "currentDestinationString",
+                new Intent(),
+                "dataManagementLabel");
 
         ShadowContextImplForBackup.sComponentBinderMap.put(mTransport1.componentName,
                 mTransport1.binder);
         ShadowContextImplForBackup.sComponentBinderMap.put(mTransport2.componentName,
                 mTransport2.binder);
-        ShadowBackupTransportStub.sBinderTransportMap.put(mTransport1.binder, mTransport1.stub);
-        ShadowBackupTransportStub.sBinderTransportMap.put(mTransport2.binder, mTransport2.stub);
+        ShadowBackupTransportStub.sBinderTransportMap.put(
+                mTransport1.binder, mTransport1.binderInterface);
+        ShadowBackupTransportStub.sBinderTransportMap.put(
+                mTransport2.binder, mTransport2.binderInterface);
     }
 
     @After
@@ -129,8 +150,10 @@
                 Arrays.asList(mTransport1.componentName, mTransport2.componentName));
         assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
                 Arrays.asList(mTransport1.name, mTransport2.name));
-        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isTrue();
-        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue();
+        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
+                .isTrue();
+        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
+                .isTrue();
     }
 
     @Test
@@ -153,8 +176,10 @@
                 Collections.singleton(mTransport2.componentName));
         assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
                 Collections.singleton(mTransport2.name));
-        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse();
-        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue();
+        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
+                .isFalse();
+        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
+                .isTrue();
     }
 
     @Test
@@ -193,8 +218,10 @@
                 Collections.singleton(mTransport2.componentName));
         assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
                 Collections.singleton(mTransport2.name));
-        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse();
-        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue();
+        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
+                .isFalse();
+        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
+                .isTrue();
     }
 
     @Test
@@ -250,8 +277,10 @@
                 Arrays.asList(mTransport1.componentName, mTransport2.componentName));
         assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
                 Arrays.asList(mTransport1.name, mTransport2.name));
-        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse();
-        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue();
+        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
+                .isFalse();
+        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
+                .isTrue();
     }
 
     @Test
@@ -265,8 +294,10 @@
                 Arrays.asList(mTransport1.componentName, mTransport2.componentName));
         assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
                 Arrays.asList(mTransport1.name, mTransport2.name));
-        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse();
-        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isFalse();
+        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
+                .isFalse();
+        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
+                .isFalse();
     }
 
     @Test
@@ -280,8 +311,10 @@
                 Arrays.asList(mTransport1.componentName, mTransport2.componentName));
         assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
                 Arrays.asList(mTransport1.name, mTransport2.name));
-        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse();
-        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isFalse();
+        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
+                .isFalse();
+        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
+                .isFalse();
     }
 
     @Test
@@ -295,8 +328,10 @@
                 Arrays.asList(mTransport1.componentName, mTransport2.componentName));
         assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
                 Arrays.asList(mTransport1.name, mTransport2.name));
-        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.stub)).isFalse();
-        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.stub)).isTrue();
+        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport1.binderInterface))
+                .isFalse();
+        assertThat(mTransportBoundListenerStub.isCalledForTransport(mTransport2.binderInterface))
+                .isTrue();
     }
 
     @Test
@@ -305,9 +340,9 @@
                 Arrays.asList(mTransport1, mTransport2), mTransport1.name);
 
         assertThat(transportManager.getTransportBinder(mTransport1.name)).isEqualTo(
-                mTransport1.stub);
+                mTransport1.binderInterface);
         assertThat(transportManager.getTransportBinder(mTransport2.name)).isEqualTo(
-                mTransport2.stub);
+                mTransport2.binderInterface);
     }
 
     @Test
@@ -326,7 +361,7 @@
 
         assertThat(transportManager.getTransportBinder(mTransport1.name)).isNull();
         assertThat(transportManager.getTransportBinder(mTransport2.name)).isEqualTo(
-                mTransport2.stub);
+                mTransport2.binderInterface);
     }
 
     @Test
@@ -356,7 +391,8 @@
         TransportManager transportManager = createTransportManagerAndSetUpTransports(
                 Arrays.asList(mTransport1, mTransport2), mTransport1.name);
 
-        assertThat(transportManager.getCurrentTransportBinder()).isEqualTo(mTransport1.stub);
+        assertThat(transportManager.getCurrentTransportBinder())
+                .isEqualTo(mTransport1.binderInterface);
     }
 
     @Test
@@ -375,8 +411,10 @@
         TransportManager transportManager = createTransportManagerAndSetUpTransports(
                 Arrays.asList(mTransport1, mTransport2), mTransport1.name);
 
-        assertThat(transportManager.getTransportName(mTransport1.stub)).isEqualTo(mTransport1.name);
-        assertThat(transportManager.getTransportName(mTransport2.stub)).isEqualTo(mTransport2.name);
+        assertThat(transportManager.getTransportName(mTransport1.binderInterface))
+                .isEqualTo(mTransport1.name);
+        assertThat(transportManager.getTransportName(mTransport2.binderInterface))
+                .isEqualTo(mTransport2.name);
     }
 
     @Test
@@ -385,8 +423,9 @@
                 createTransportManagerAndSetUpTransports(Collections.singletonList(mTransport2),
                         Collections.singletonList(mTransport1), mTransport1.name);
 
-        assertThat(transportManager.getTransportName(mTransport1.stub)).isNull();
-        assertThat(transportManager.getTransportName(mTransport2.stub)).isEqualTo(mTransport2.name);
+        assertThat(transportManager.getTransportName(mTransport1.binderInterface)).isNull();
+        assertThat(transportManager.getTransportName(mTransport2.binderInterface))
+                .isEqualTo(mTransport2.name);
     }
 
     @Test
@@ -499,7 +538,7 @@
         TransportManager transportManager =
                 createTransportManagerAndSetUpTransports(
                         Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-        transportManager.describeTransport(
+        transportManager.updateTransportAttributes(
                 mTransport1.componentName, "newName", null, "destinationString", null, null);
 
         TransportClient transportClient =
@@ -514,7 +553,7 @@
         TransportManager transportManager =
                 createTransportManagerAndSetUpTransports(
                         Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-        transportManager.describeTransport(
+        transportManager.updateTransportAttributes(
                 mTransport1.componentName, "newName", null, "destinationString", null, null);
 
         TransportClient transportClient =
@@ -529,7 +568,7 @@
         TransportManager transportManager =
                 createTransportManagerAndSetUpTransports(
                         Arrays.asList(mTransport1, mTransport2), mTransport1.name);
-        transportManager.describeTransport(
+        transportManager.updateTransportAttributes(
                 mTransport1.componentName, "newName", null, "destinationString", null, null);
 
         String transportName = transportManager.getTransportName(mTransport1.componentName);
@@ -549,6 +588,48 @@
         assertThat(transportManager.isTransportRegistered(mTransport2.name)).isFalse();
     }
 
+    @Test
+    public void getTransportAttributes_forRegisteredTransport_returnsCorrectValues()
+            throws Exception {
+        TransportManager transportManager =
+                createTransportManagerAndSetUpTransports(
+                        Collections.singletonList(mTransport1),
+                        mTransport1.name);
+
+        assertThat(transportManager.getTransportConfigurationIntent(mTransport1.name))
+                .isEqualTo(mTransport1.binderInterface.configurationIntent());
+        assertThat(transportManager.getTransportDataManagementIntent(mTransport1.name))
+                .isEqualTo(mTransport1.binderInterface.dataManagementIntent());
+        assertThat(transportManager.getTransportDataManagementLabel(mTransport1.name))
+                .isEqualTo(mTransport1.binderInterface.dataManagementLabel());
+        assertThat(transportManager.getTransportDirName(mTransport1.name))
+                .isEqualTo(mTransport1.binderInterface.transportDirName());
+    }
+
+    @Test
+    public void getTransportAttributes_forUnregisteredTransport_throws()
+            throws Exception {
+        TransportManager transportManager =
+                createTransportManagerAndSetUpTransports(
+                        Collections.singletonList(mTransport1),
+                        Collections.singletonList(mTransport2),
+                        mTransport1.name);
+
+        expectThrows(
+                TransportNotRegisteredException.class,
+                () -> transportManager.getTransportConfigurationIntent(mTransport2.name));
+        expectThrows(
+                TransportNotRegisteredException.class,
+                () -> transportManager.getTransportDataManagementIntent(
+                        mTransport2.name));
+        expectThrows(
+                TransportNotRegisteredException.class,
+                () -> transportManager.getTransportDataManagementLabel(mTransport2.name));
+        expectThrows(
+                TransportNotRegisteredException.class,
+                () -> transportManager.getTransportDirName(mTransport2.name));
+    }
+
     private void setUpPackageWithTransports(String packageName, List<TransportInfo> transports,
             int flags) throws Exception {
         PackageInfo packageInfo = new PackageInfo();
@@ -616,10 +697,12 @@
         assertThat(transportManager.getBoundTransportNames()).asList().containsExactlyElementsIn(
                 availableTransportsNames);
         for (TransportInfo transport : availableTransports) {
-            assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.stub)).isTrue();
+            assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.binderInterface))
+                    .isTrue();
         }
         for (TransportInfo transport : unavailableTransports) {
-            assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.stub)).isFalse();
+            assertThat(mTransportBoundListenerStub.isCalledForTransport(transport.binderInterface))
+                    .isFalse();
         }
 
         mTransportBoundListenerStub.resetState();
@@ -627,19 +710,46 @@
         return transportManager;
     }
 
+    private static <T extends Throwable> void expectThrows(
+            Class<T> throwableClass, ThrowingRunnable runnable) {
+        try {
+            runnable.runOrThrow();
+            fail("Expected to throw " + throwableClass.getSimpleName());
+        } catch (Throwable t) {
+            assertThat(t).isInstanceOf(throwableClass);
+        }
+    }
+
     private static class TransportInfo {
         public final String packageName;
         public final String name;
         public final ComponentName componentName;
-        public final BackupTransportStub stub;
+        public final IBackupTransport binderInterface;
         public final IBinder binder;
 
-        TransportInfo(String packageName, String name) {
+        TransportInfo(
+                String packageName,
+                String name,
+                @Nullable Intent configurationIntent,
+                String currentDestinationString,
+                @Nullable Intent dataManagementIntent,
+                String dataManagementLabel) {
             this.packageName = packageName;
             this.name = name;
             this.componentName = new ComponentName(packageName, name);
-            this.stub = new BackupTransportStub(name);
             this.binder = mock(IBinder.class);
+            IBackupTransport transport = mock(IBackupTransport.class);
+            try {
+                when(transport.name()).thenReturn(name);
+                when(transport.configurationIntent()).thenReturn(configurationIntent);
+                when(transport.currentDestinationString()).thenReturn(currentDestinationString);
+                when(transport.dataManagementIntent()).thenReturn(dataManagementIntent);
+                when(transport.dataManagementLabel()).thenReturn(dataManagementLabel);
+            } catch (RemoteException e) {
+                // Only here to mock methods that throw RemoteException
+            }
+            this.binderInterface = transport;
         }
     }
+
 }
diff --git a/services/robotests/src/com/android/server/backup/testing/BackupTransportStub.java b/services/robotests/src/com/android/server/backup/testing/BackupTransportStub.java
deleted file mode 100644
index ec09f90..0000000
--- a/services/robotests/src/com/android/server/backup/testing/BackupTransportStub.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.backup.testing;
-
-import android.app.backup.RestoreDescription;
-import android.app.backup.RestoreSet;
-import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-
-import com.android.internal.backup.IBackupTransport;
-
-/**
- * Stub backup transport, doing nothing and returning default values.
- */
-public class BackupTransportStub implements IBackupTransport {
-
-    private final String mName;
-
-    public BackupTransportStub(String name) {
-        mName = name;
-    }
-
-    @Override
-    public IBinder asBinder() {
-        return null;
-    }
-
-    @Override
-    public String name() throws RemoteException {
-        return mName;
-    }
-
-    @Override
-    public Intent configurationIntent() throws RemoteException {
-        return null;
-    }
-
-    @Override
-    public String currentDestinationString() throws RemoteException {
-        return null;
-    }
-
-    @Override
-    public Intent dataManagementIntent() throws RemoteException {
-        return null;
-    }
-
-    @Override
-    public String dataManagementLabel() throws RemoteException {
-        return null;
-    }
-
-    @Override
-    public String transportDirName() throws RemoteException {
-        return null;
-    }
-
-    @Override
-    public long requestBackupTime() throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public int initializeDevice() throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor inFd, int flags)
-            throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public int clearBackupData(PackageInfo packageInfo) throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public int finishBackup() throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public RestoreSet[] getAvailableRestoreSets() throws RemoteException {
-        return new RestoreSet[0];
-    }
-
-    @Override
-    public long getCurrentRestoreSet() throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public int startRestore(long token, PackageInfo[] packages) throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public RestoreDescription nextRestorePackage() throws RemoteException {
-        return null;
-    }
-
-    @Override
-    public int getRestoreData(ParcelFileDescriptor outFd) throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public void finishRestore() throws RemoteException {
-
-    }
-
-    @Override
-    public long requestFullBackupTime() throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket,
-            int flags)
-            throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public int checkFullBackupSize(long size) throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public int sendBackupData(int numBytes) throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public void cancelFullBackup() throws RemoteException {
-
-    }
-
-    @Override
-    public boolean isAppEligibleForBackup(PackageInfo targetPackage, boolean isFullBackup)
-            throws RemoteException {
-        return false;
-    }
-
-    @Override
-    public long getBackupQuota(String packageName, boolean isFullBackup)
-            throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) throws RemoteException {
-        return 0;
-    }
-
-    @Override
-    public int abortFullRestore() throws RemoteException {
-        return 0;
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index dbebd01..45ecbfb 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -153,13 +153,14 @@
     }
 
     @Test
-    public void uiAutomationBinderDiesBeforeConnecting_shouldNotCrash() throws Exception {
+    public void uiAutomationBinderDiesBeforeConnecting_notifiesSystem() throws Exception {
         register(0);
         ArgumentCaptor<IBinder.DeathRecipient> captor = ArgumentCaptor.forClass(
                 IBinder.DeathRecipient.class);
         verify(mMockOwner).linkToDeath(captor.capture(), anyInt());
         captor.getValue().binderDied();
         mMessageCapturingHandler.sendAllMessages();
+        verify(mMockSystemSupport).onClientChange(false);
     }
 
     private void register(int flags) {
diff --git a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
index 5520bd7..c91e22f 100644
--- a/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/TaskRecordTests.java
@@ -17,33 +17,46 @@
 
 package com.android.server.am;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
 
 import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
+import android.content.res.XmlResourceParser;
+import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.service.voice.IVoiceInteractionSession;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.MediumTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.Xml;
 
+import com.android.frameworks.servicestests.R;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.server.am.TaskRecord.TaskRecordFactory;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
 
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
 import java.io.IOException;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.nio.file.Files;
 import java.util.ArrayList;
+import java.util.Comparator;
 
 /**
  * Tests for exercising {@link TaskRecord}.
@@ -54,11 +67,33 @@
 @MediumTest
 @Presubmit
 @RunWith(AndroidJUnit4.class)
-public class TaskRecordTests {
+public class TaskRecordTests extends ActivityTestsBase {
+
+    private static final String TASK_TAG = "task";
+
+    private ActivityManagerService mService;
 
     @Before
     public void setUp() throws Exception {
+        super.setUp();
         TaskRecord.setTaskRecordFactory(null);
+        mService = createActivityManagerService();
+    }
+
+    @Test
+    public void testRestoreWindowedTask() throws Exception {
+        final TaskRecord expected = createTaskRecord(64);
+        expected.mLastNonFullscreenBounds = new Rect(50, 50, 100, 100);
+
+        final File serializedFile = serializeToFile(expected);
+
+        try {
+            final TaskRecord actual = restoreFromFile(serializedFile);
+            assertEquals(expected.taskId, actual.taskId);
+            assertEquals(expected.mLastNonFullscreenBounds, actual.mLastNonFullscreenBounds);
+        } finally {
+            serializedFile.delete();
+        }
     }
 
     @Test
@@ -78,6 +113,38 @@
         assertTrue(factory.mCreated);
     }
 
+    private File serializeToFile(TaskRecord r) throws IOException, XmlPullParserException {
+        final File tmpFile = File.createTempFile(r.taskId + "_task_", "xml");
+
+        try (final OutputStream os = new FileOutputStream(tmpFile)) {
+            final XmlSerializer serializer = Xml.newSerializer();
+            serializer.setOutput(os, "UTF-8");
+            serializer.startDocument(null, true);
+            serializer.startTag(null, TASK_TAG);
+            r.saveToXml(serializer);
+            serializer.endTag(null, TASK_TAG);
+            serializer.endDocument();
+        }
+
+        return tmpFile;
+    }
+
+    private TaskRecord restoreFromFile(File file) throws IOException, XmlPullParserException {
+        try (final Reader reader = new BufferedReader(new FileReader(file))) {
+            final XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(reader);
+            assertEquals(XmlPullParser.START_TAG, parser.next());
+            assertEquals(TASK_TAG, parser.getName());
+            return TaskRecord.restoreFromXml(parser, mService.mStackSupervisor);
+        }
+    }
+
+    private TaskRecord createTaskRecord(int taskId) {
+        return new TaskRecord(mService, taskId, new Intent(), null, null, null, null, null, false,
+                false, false, 0, 10050, null, new ArrayList<>(), 0, false, null, 0, 0, 0, 0, 0,
+                null, 0, false, false, false, 0, 0);
+    }
+
     private static class TestTaskRecordFactory extends TaskRecordFactory {
         private boolean mCreated = false;
 
diff --git a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
index 362856c..f4c5442 100644
--- a/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/BackupManagerServiceTest.java
@@ -117,7 +117,7 @@
                 "dataManagementLabel");
 
         verify(mTransportManager)
-                .describeTransport(
+                .updateTransportAttributes(
                         eq(TRANSPORT_COMPONENT),
                         eq(TRANSPORT_NAME),
                         eq(configurationIntent),
@@ -247,7 +247,7 @@
                 null);
 
         verify(mTransportManager)
-                .describeTransport(
+                .updateTransportAttributes(
                         eq(TRANSPORT_COMPONENT),
                         eq(TRANSPORT_NAME),
                         eq(configurationIntent),
@@ -274,7 +274,7 @@
                 "dataManagementLabel");
 
         verify(mTransportManager)
-                .describeTransport(
+                .updateTransportAttributes(
                         eq(TRANSPORT_COMPONENT),
                         eq(TRANSPORT_NAME),
                         eq(configurationIntent),
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
new file mode 100644
index 0000000..c918e8c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/KeySyncUtilsTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings.recoverablekeystore;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.util.Arrays;
+
+import javax.crypto.SecretKey;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KeySyncUtilsTest {
+    private static final int RECOVERY_KEY_LENGTH_BITS = 256;
+    private static final int THM_KF_HASH_SIZE = 256;
+    private static final String SHA_256_ALGORITHM = "SHA-256";
+
+    @Test
+    public void calculateThmKfHash_isShaOfLockScreenHashWithPrefix() throws Exception {
+        byte[] lockScreenHash = utf8Bytes("012345678910");
+
+        byte[] thmKfHash = KeySyncUtils.calculateThmKfHash(lockScreenHash);
+
+        assertArrayEquals(calculateSha256(utf8Bytes("THM_KF_hash012345678910")), thmKfHash);
+    }
+
+    @Test
+    public void calculateThmKfHash_is256BitsLong() throws Exception {
+        byte[] thmKfHash = KeySyncUtils.calculateThmKfHash(utf8Bytes("1234"));
+
+        assertEquals(THM_KF_HASH_SIZE / Byte.SIZE, thmKfHash.length);
+    }
+
+    @Test
+    public void generateRecoveryKey_returnsA256BitKey() throws Exception {
+        SecretKey key = KeySyncUtils.generateRecoveryKey();
+
+        assertEquals(RECOVERY_KEY_LENGTH_BITS / Byte.SIZE, key.getEncoded().length);
+    }
+
+    @Test
+    public void generateRecoveryKey_generatesANewKeyEachTime() throws Exception {
+        SecretKey a = KeySyncUtils.generateRecoveryKey();
+        SecretKey b = KeySyncUtils.generateRecoveryKey();
+
+        assertFalse(Arrays.equals(a.getEncoded(), b.getEncoded()));
+    }
+
+    private static byte[] utf8Bytes(String s) {
+        return s.getBytes(StandardCharsets.UTF_8);
+    }
+
+    private static byte[] calculateSha256(byte[] bytes) throws Exception {
+        MessageDigest messageDigest = MessageDigest.getInstance(SHA_256_ALGORITHM);
+        messageDigest.update(bytes);
+        return messageDigest.digest();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageImplTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageImplTest.java
new file mode 100644
index 0000000..fb4e75e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStorageImplTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings.recoverablekeystore;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.nio.charset.StandardCharsets;
+import java.security.InvalidKeyException;
+import java.security.KeyStoreException;
+import java.util.Random;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RecoverableKeyStorageImplTest {
+    private static final String KEY_ALGORITHM = "AES";
+    private static final int GCM_TAG_LENGTH_BYTES = 16;
+    private static final int BITS_PER_BYTE = 8;
+    private static final int GCM_TAG_LENGTH_BITS = GCM_TAG_LENGTH_BYTES * BITS_PER_BYTE;
+    private static final int GCM_NONCE_LENGTH_BYTES = 12;
+    private static final String TEST_KEY_ALIAS = "RecoverableKeyStorageImplTestKey";
+    private static final int KEYSTORE_UID_SELF = -1;
+
+    private RecoverableKeyStorageImpl mRecoverableKeyStorage;
+
+    @Before
+    public void setUp() throws Exception {
+        mRecoverableKeyStorage = RecoverableKeyStorageImpl.newInstance(
+                /*userId=*/ KEYSTORE_UID_SELF);
+    }
+
+    @After
+    public void tearDown() {
+        try {
+            mRecoverableKeyStorage.removeFromAndroidKeyStore(TEST_KEY_ALIAS);
+        } catch (KeyStoreException e) {
+            // Do nothing.
+        }
+    }
+
+    @Test
+    public void loadFromAndroidKeyStore_loadsAKeyThatWasImported() throws Exception {
+        SecretKey key = generateKey();
+        mRecoverableKeyStorage.importIntoAndroidKeyStore(
+                TEST_KEY_ALIAS,
+                key,
+                getKeyProperties());
+
+        assertKeysAreEquivalent(
+                key, mRecoverableKeyStorage.loadFromAndroidKeyStore(TEST_KEY_ALIAS));
+    }
+
+    @Test
+    public void importIntoAndroidKeyStore_importsWithKeyProperties() throws Exception {
+        mRecoverableKeyStorage.importIntoAndroidKeyStore(
+                TEST_KEY_ALIAS,
+                generateKey(),
+                getKeyProperties());
+
+        SecretKey key = mRecoverableKeyStorage.loadFromAndroidKeyStore(TEST_KEY_ALIAS);
+
+        Mac mac = Mac.getInstance("HmacSHA256");
+        try {
+            // Fails because missing PURPOSE_SIGN or PURPOSE_VERIFY
+            mac.init(key);
+            fail("Was able to initialize Mac with an ENCRYPT/DECRYPT-only key.");
+        } catch (InvalidKeyException e) {
+            // expect exception
+        }
+    }
+
+    @Test
+    public void removeFromAndroidKeyStore_removesAnEntry() throws Exception {
+        mRecoverableKeyStorage.importIntoAndroidKeyStore(
+                TEST_KEY_ALIAS,
+                generateKey(),
+                getKeyProperties());
+
+        mRecoverableKeyStorage.removeFromAndroidKeyStore(TEST_KEY_ALIAS);
+
+        assertNull(mRecoverableKeyStorage.loadFromAndroidKeyStore(TEST_KEY_ALIAS));
+    }
+
+    private static KeyProtection getKeyProperties() {
+        return new KeyProtection.Builder(
+                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                .build();
+    }
+
+    /**
+     * Asserts that {@code b} key can decrypt data encrypted with {@code a} key. Otherwise throws.
+     */
+    private static void assertKeysAreEquivalent(SecretKey a, SecretKey b) throws Exception {
+        byte[] plaintext = "doge".getBytes(StandardCharsets.UTF_8);
+        byte[] nonce = generateGcmNonce();
+
+        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
+        cipher.init(Cipher.ENCRYPT_MODE, a, new GCMParameterSpec(GCM_TAG_LENGTH_BITS, nonce));
+        byte[] encrypted = cipher.doFinal(plaintext);
+
+        cipher.init(Cipher.DECRYPT_MODE, b, new GCMParameterSpec(GCM_TAG_LENGTH_BITS, nonce));
+        byte[] decrypted = cipher.doFinal(encrypted);
+
+        assertArrayEquals(decrypted, plaintext);
+    }
+
+    /**
+     * Returns a new random GCM nonce.
+     */
+    private static byte[] generateGcmNonce() {
+        Random random = new Random();
+        byte[] nonce = new byte[GCM_NONCE_LENGTH_BYTES];
+        random.nextBytes(nonce);
+        return nonce;
+    }
+
+    private static SecretKey generateKey() throws Exception {
+        KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
+        keyGenerator.init(/*keySize=*/ 256);
+        return keyGenerator.generateKey();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
index 4cd5631..fa73722 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/WrappedKeyTest.java
@@ -16,7 +16,9 @@
 
 package com.android.server.locksettings.recoverablekeystore;
 
+import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import android.security.keystore.AndroidKeyStoreSecretKey;
 import android.security.keystore.KeyGenParameterSpec;
@@ -29,6 +31,8 @@
 import org.junit.runner.RunWith;
 
 import java.security.KeyStore;
+import java.util.HashMap;
+import java.util.Map;
 
 import javax.crypto.Cipher;
 import javax.crypto.KeyGenerator;
@@ -70,6 +74,36 @@
         assertEquals(rawKey, unwrappedKey);
     }
 
+    @Test
+    public void decryptWrappedKeys_decryptsWrappedKeys() throws Exception {
+        String alias = "karlin";
+        SecretKey platformKey = generateAndroidKeyStoreKey();
+        SecretKey appKey = generateKey();
+        WrappedKey wrappedKey = WrappedKey.fromSecretKey(platformKey, appKey);
+        HashMap<String, WrappedKey> keysByAlias = new HashMap<>();
+        keysByAlias.put(alias, wrappedKey);
+
+        Map<String, SecretKey> unwrappedKeys = WrappedKey.unwrapKeys(platformKey, keysByAlias);
+
+        assertEquals(1, unwrappedKeys.size());
+        assertTrue(unwrappedKeys.containsKey(alias));
+        assertArrayEquals(appKey.getEncoded(), unwrappedKeys.get(alias).getEncoded());
+    }
+
+    @Test
+    public void decryptWrappedKeys_doesNotDieIfSomeKeysAreUnwrappable() throws Exception {
+        String alias = "karlin";
+        SecretKey appKey = generateKey();
+        WrappedKey wrappedKey = WrappedKey.fromSecretKey(generateKey(), appKey);
+        HashMap<String, WrappedKey> keysByAlias = new HashMap<>();
+        keysByAlias.put(alias, wrappedKey);
+
+        Map<String, SecretKey> unwrappedKeys = WrappedKey.unwrapKeys(
+                generateAndroidKeyStoreKey(), keysByAlias);
+
+        assertEquals(0, unwrappedKeys.size());
+    }
+
     private SecretKey generateKey() throws Exception {
         KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
         keyGenerator.init(/*keySize=*/ 256);
@@ -81,7 +115,8 @@
                 KEY_ALGORITHM,
                 ANDROID_KEY_STORE_PROVIDER);
         keyGenerator.init(new KeyGenParameterSpec.Builder(
-                WRAPPING_KEY_ALIAS, KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                WRAPPING_KEY_ALIAS,
+                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                 .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                 .build());
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index fafae6c..b073ee5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -305,7 +305,6 @@
                 null /*usesStaticLibrariesVersions*/);
         assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
         assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
-        assertThat(testPkgSetting01.origPackage, is(nullValue()));
         assertThat(testPkgSetting01.pkgFlags, is(0));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
         final PackageUserState userState = testPkgSetting01.readUserState(0);
@@ -339,7 +338,6 @@
                 null /*usesStaticLibrariesVersions*/);
         assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
         assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
-        assertThat(testPkgSetting01.origPackage, is(nullValue()));
         assertThat(testPkgSetting01.pkgFlags, is(ApplicationInfo.FLAG_SYSTEM));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
         final PackageUserState userState = testPkgSetting01.readUserState(0);
@@ -419,7 +417,6 @@
         assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
         assertThat(testPkgSetting01.resourcePath, is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.secondaryCpuAbiString, is("armeabi"));
-        assertSame(testPkgSetting01.origPackage, originalPkgSetting01);
         // signatures object must be different
         assertNotSame(testPkgSetting01.signatures, originalSignatures);
         assertThat(testPkgSetting01.versionCode, is(UPDATED_VERSION_CODE));
@@ -457,7 +454,6 @@
         assertThat(testPkgSetting01.appId, is(0));
         assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
-        assertThat(testPkgSetting01.origPackage, is(nullValue()));
         assertThat(testPkgSetting01.pkgFlags, is(0));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
         assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64"));
@@ -506,7 +502,6 @@
         assertThat(testPkgSetting01.appId, is(10064));
         assertThat(testPkgSetting01.codePath, is(INITIAL_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
-        assertThat(testPkgSetting01.origPackage, is(nullValue()));
         assertThat(testPkgSetting01.pkgFlags, is(0));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
         assertThat(testPkgSetting01.primaryCpuAbiString, is("x86_64"));
@@ -551,7 +546,6 @@
         assertThat(testPkgSetting01.appId, is(10064));
         assertThat(testPkgSetting01.codePath, is(UPDATED_CODE_PATH));
         assertThat(testPkgSetting01.name, is(PACKAGE_NAME));
-        assertThat(testPkgSetting01.origPackage, is(nullValue()));
         assertThat(testPkgSetting01.pkgFlags, is(0));
         assertThat(testPkgSetting01.pkgPrivateFlags, is(0));
         assertThat(testPkgSetting01.primaryCpuAbiString, is("arm64-v8a"));
@@ -658,8 +652,6 @@
         // oldCodePaths is _not_ copied
         // assertNotSame(origPkgSetting.oldCodePaths, testPkgSetting.oldCodePaths);
         // assertThat(origPkgSetting.oldCodePaths, is(not(testPkgSetting.oldCodePaths)));
-        assertSame(origPkgSetting.origPackage, testPkgSetting.origPackage);
-        assertThat(origPkgSetting.origPackage, is(testPkgSetting.origPackage));
         assertSame(origPkgSetting.parentPackageName, testPkgSetting.parentPackageName);
         assertThat(origPkgSetting.parentPackageName, is(testPkgSetting.parentPackageName));
         assertSame(origPkgSetting.pkg, testPkgSetting.pkg);
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 86ce90c..b792d82 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -18,9 +18,14 @@
 
 import static android.app.usage.UsageEvents.Event.NOTIFICATION_SEEN;
 import static android.app.usage.UsageEvents.Event.USER_INTERACTION;
+import static android.app.usage.UsageStatsManager.REASON_DEFAULT;
+import static android.app.usage.UsageStatsManager.REASON_FORCED;
 import static android.app.usage.UsageStatsManager.REASON_PREDICTED;
+import static android.app.usage.UsageStatsManager.REASON_TIMEOUT;
+import static android.app.usage.UsageStatsManager.REASON_USAGE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
 
@@ -79,6 +84,7 @@
     private static final long RARE_THRESHOLD = 48 * HOUR_MS;
 
     private MyInjector mInjector;
+    private AppStandbyController mController;
 
     static class MyContextWrapper extends ContextWrapper {
         PackageManager mockPm = mock(PackageManager.class);
@@ -237,24 +243,23 @@
     public void setUp() throws Exception {
         MyContextWrapper myContext = new MyContextWrapper(InstrumentationRegistry.getContext());
         mInjector = new MyInjector(myContext, Looper.getMainLooper());
+        mController = setupController();
     }
 
     @Test
     public void testCharging() throws Exception {
-        AppStandbyController controller = setupController();
-
-        setChargingState(controller, true);
+        setChargingState(mController, true);
         mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
-        assertFalse(controller.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
+        assertFalse(mController.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
                 mInjector.mElapsedRealtime, false));
 
-        setChargingState(controller, false);
+        setChargingState(mController, false);
         mInjector.mElapsedRealtime = 2 * RARE_THRESHOLD + 2;
-        controller.checkIdleStates(USER_ID);
-        assertTrue(controller.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
+        mController.checkIdleStates(USER_ID);
+        assertTrue(mController.isAppIdleFilteredOrParoled(PACKAGE_1, USER_ID,
                 mInjector.mElapsedRealtime, false));
-        setChargingState(controller, true);
-        assertFalse(controller.isAppIdleFilteredOrParoled(PACKAGE_1,USER_ID,
+        setChargingState(mController, true);
+        assertFalse(mController.isAppIdleFilteredOrParoled(PACKAGE_1,USER_ID,
                 mInjector.mElapsedRealtime, false));
     }
 
@@ -282,112 +287,142 @@
 
     @Test
     public void testBuckets() throws Exception {
-        AppStandbyController controller = setupController();
+        assertTimeout(mController, 0, STANDBY_BUCKET_NEVER);
 
-        assertTimeout(controller, 0, UsageStatsManager.STANDBY_BUCKET_NEVER);
-
-        reportEvent(controller, USER_INTERACTION, 0);
+        reportEvent(mController, USER_INTERACTION, 0);
 
         // ACTIVE bucket
-        assertTimeout(controller, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
+        assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
 
         // WORKING_SET bucket
-        assertTimeout(controller, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
+        assertTimeout(mController, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
 
         // WORKING_SET bucket
-        assertTimeout(controller, FREQUENT_THRESHOLD - 1, STANDBY_BUCKET_WORKING_SET);
+        assertTimeout(mController, FREQUENT_THRESHOLD - 1, STANDBY_BUCKET_WORKING_SET);
 
         // FREQUENT bucket
-        assertTimeout(controller, FREQUENT_THRESHOLD + 1, STANDBY_BUCKET_FREQUENT);
+        assertTimeout(mController, FREQUENT_THRESHOLD + 1, STANDBY_BUCKET_FREQUENT);
 
         // RARE bucket
-        assertTimeout(controller, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE);
+        assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_RARE);
 
-        reportEvent(controller, USER_INTERACTION, RARE_THRESHOLD + 1);
+        reportEvent(mController, USER_INTERACTION, RARE_THRESHOLD + 1);
 
-        assertTimeout(controller, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE);
+        assertTimeout(mController, RARE_THRESHOLD + 1, STANDBY_BUCKET_ACTIVE);
 
         // RARE bucket
-        assertTimeout(controller, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
+        assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
     }
 
     @Test
     public void testScreenTimeAndBuckets() throws Exception {
-        AppStandbyController controller = setupController();
         mInjector.setDisplayOn(false);
 
-        assertTimeout(controller, 0, UsageStatsManager.STANDBY_BUCKET_NEVER);
+        assertTimeout(mController, 0, STANDBY_BUCKET_NEVER);
 
-        reportEvent(controller, USER_INTERACTION, 0);
+        reportEvent(mController, USER_INTERACTION, 0);
 
         // ACTIVE bucket
-        assertTimeout(controller, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
+        assertTimeout(mController, WORKING_SET_THRESHOLD - 1, STANDBY_BUCKET_ACTIVE);
 
         // WORKING_SET bucket
-        assertTimeout(controller, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
+        assertTimeout(mController, WORKING_SET_THRESHOLD + 1, STANDBY_BUCKET_WORKING_SET);
 
         // RARE bucket, should fail because the screen wasn't ON.
         mInjector.mElapsedRealtime = RARE_THRESHOLD + 1;
-        controller.checkIdleStates(USER_ID);
-        assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller));
+        mController.checkIdleStates(USER_ID);
+        assertNotEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
 
         mInjector.setDisplayOn(true);
-        assertTimeout(controller, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
+        assertTimeout(mController, RARE_THRESHOLD * 2 + 2, STANDBY_BUCKET_RARE);
     }
 
     @Test
     public void testForcedIdle() throws Exception {
-        AppStandbyController controller = setupController();
-        setChargingState(controller, false);
+        setChargingState(mController, false);
 
-        controller.forceIdleState(PACKAGE_1, USER_ID, true);
-        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller));
-        assertTrue(controller.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
+        mController.forceIdleState(PACKAGE_1, USER_ID, true);
+        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
+        assertTrue(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
 
-        controller.forceIdleState(PACKAGE_1, USER_ID, false);
-        assertEquals(STANDBY_BUCKET_ACTIVE, controller.getAppStandbyBucket(PACKAGE_1, USER_ID, 0,
+        mController.forceIdleState(PACKAGE_1, USER_ID, false);
+        assertEquals(STANDBY_BUCKET_ACTIVE, mController.getAppStandbyBucket(PACKAGE_1, USER_ID, 0,
                 true));
-        assertFalse(controller.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
+        assertFalse(mController.isAppIdleFiltered(PACKAGE_1, UID_1, USER_ID, 0));
     }
 
     @Test
     public void testNotificationEvent() throws Exception {
-        AppStandbyController controller = setupController();
-        setChargingState(controller, false);
+        setChargingState(mController, false);
 
-        reportEvent(controller, USER_INTERACTION, 0);
-        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller));
+        reportEvent(mController, USER_INTERACTION, 0);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
         mInjector.mElapsedRealtime = 1;
-        reportEvent(controller, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
-        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller));
+        reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
 
-        controller.forceIdleState(PACKAGE_1, USER_ID, true);
-        reportEvent(controller, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
-        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(controller));
+        mController.forceIdleState(PACKAGE_1, USER_ID, true);
+        reportEvent(mController, NOTIFICATION_SEEN, mInjector.mElapsedRealtime);
+        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController));
     }
 
     @Test
     public void testPredictionTimedout() throws Exception {
-        AppStandbyController controller = setupController();
-        setChargingState(controller, false);
-        controller.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
-                REASON_PREDICTED + "CTS", 1 * HOUR_MS);
+        setChargingState(mController, false);
+        // Set it to timeout or usage, so that prediction can override it
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_RARE,
+                REASON_TIMEOUT, 1 * HOUR_MS);
+        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
+
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+                REASON_PREDICTED + ":CTS", 1 * HOUR_MS);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
 
         // Fast forward 12 hours
         mInjector.mElapsedRealtime += WORKING_SET_THRESHOLD;
-        controller.checkIdleStates(USER_ID);
+        mController.checkIdleStates(USER_ID);
         // Should still be in predicted bucket, since prediction timeout is 1 day since prediction
-        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(controller));
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
         // Fast forward two more hours
         mInjector.mElapsedRealtime += 2 * HOUR_MS;
-        controller.checkIdleStates(USER_ID);
+        mController.checkIdleStates(USER_ID);
         // Should have now applied prediction timeout
-        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(controller));
+        assertEquals(STANDBY_BUCKET_WORKING_SET, getStandbyBucket(mController));
 
         // Fast forward RARE bucket
         mInjector.mElapsedRealtime += RARE_THRESHOLD;
-        controller.checkIdleStates(USER_ID);
+        mController.checkIdleStates(USER_ID);
         // Should continue to apply prediction timeout
-        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(controller));
+        assertEquals(STANDBY_BUCKET_RARE, getStandbyBucket(mController));
+    }
+
+    @Test
+    public void testOverrides() throws Exception {
+        setChargingState(mController, false);
+        // Can force to NEVER
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
+                REASON_FORCED, 1 * HOUR_MS);
+        assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
+
+        // Prediction can't override FORCED reason
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_FREQUENT,
+                REASON_FORCED, 1 * HOUR_MS);
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_WORKING_SET,
+                REASON_PREDICTED, 1 * HOUR_MS);
+        assertEquals(STANDBY_BUCKET_FREQUENT, getStandbyBucket(mController));
+
+        // Prediction can't override NEVER
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
+                REASON_DEFAULT, 2 * HOUR_MS);
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+                REASON_PREDICTED, 2 * HOUR_MS);
+        assertEquals(STANDBY_BUCKET_NEVER, getStandbyBucket(mController));
+
+        // Prediction can't set to NEVER
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_ACTIVE,
+                REASON_USAGE, 2 * HOUR_MS);
+        mController.setAppStandbyBucket(PACKAGE_1, USER_ID, STANDBY_BUCKET_NEVER,
+                REASON_PREDICTED, 2 * HOUR_MS);
+        assertEquals(STANDBY_BUCKET_ACTIVE, getStandbyBucket(mController));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
index 89447a9..6070516 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskPositioningControllerTests.java
@@ -33,6 +33,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+
 /**
  * Tests for the {@link TaskPositioningController} class.
  *
@@ -78,9 +79,9 @@
             assertNotNull(mTarget.getDragWindowHandleLocked());
         }
 
-        assertTrue(sWm.mH.runWithScissors(() -> {
-            mTarget.finishPositioning();
-        }, TIMEOUT_MS));
+        mTarget.finishTaskPositioning();
+        // Wait until the looper processes finishTaskPositioning.
+        assertTrue(sWm.mH.runWithScissors(() -> {}, TIMEOUT_MS));
 
         assertFalse(mTarget.isPositioningLocked());
         assertNull(mTarget.getDragWindowHandleLocked());
@@ -99,15 +100,17 @@
         assertNotNull(mWindow.getTask().getTopVisibleAppMainWindow());
 
         mTarget.handleTapOutsideTask(content, 0, 0);
+        // Wait until the looper processes finishTaskPositioning.
+        assertTrue(sWm.mH.runWithScissors(() -> {}, TIMEOUT_MS));
 
         synchronized (sWm.mWindowMap) {
             assertTrue(mTarget.isPositioningLocked());
             assertNotNull(mTarget.getDragWindowHandleLocked());
         }
 
-        assertTrue(sWm.mH.runWithScissors(() -> {
-            mTarget.finishPositioning();
-        }, TIMEOUT_MS));
+        mTarget.finishTaskPositioning();
+        // Wait until the looper processes finishTaskPositioning.
+        assertTrue(sWm.mH.runWithScissors(() -> {}, TIMEOUT_MS));
 
         assertFalse(mTarget.isPositioningLocked());
         assertNull(mTarget.getDragWindowHandleLocked());
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 46efbd0..cc0259d 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -24,6 +24,7 @@
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
+import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
 import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
 
@@ -133,7 +134,7 @@
     @GuardedBy("mAppIdleLock")
     private AppIdleHistory mAppIdleHistory;
 
-    @GuardedBy("mAppIdleLock")
+    @GuardedBy("mPackageAccessListeners")
     private ArrayList<AppIdleStateChangeListener>
             mPackageAccessListeners = new ArrayList<>();
 
@@ -592,7 +593,7 @@
     }
 
     void addListener(AppIdleStateChangeListener listener) {
-        synchronized (mAppIdleLock) {
+        synchronized (mPackageAccessListeners) {
             if (!mPackageAccessListeners.contains(listener)) {
                 mPackageAccessListeners.add(listener);
             }
@@ -600,7 +601,7 @@
     }
 
     void removeListener(AppIdleStateChangeListener listener) {
-        synchronized (mAppIdleLock) {
+        synchronized (mPackageAccessListeners) {
             mPackageAccessListeners.remove(listener);
         }
     }
@@ -789,6 +790,19 @@
     void setAppStandbyBucket(String packageName, int userId, @StandbyBuckets int newBucket,
             String reason, long elapsedRealtime) {
         synchronized (mAppIdleLock) {
+            AppIdleHistory.AppUsageHistory app = mAppIdleHistory.getAppUsageHistory(packageName,
+                    userId, elapsedRealtime);
+            boolean predicted = reason != null && reason.startsWith(REASON_PREDICTED);
+            // Don't allow changing bucket if higher than ACTIVE
+            if (app.currentBucket < STANDBY_BUCKET_ACTIVE) return;
+            // Don't allow prediction to change from or to NEVER
+            if ((app.currentBucket == STANDBY_BUCKET_NEVER
+                    || newBucket == STANDBY_BUCKET_NEVER)
+                    && predicted) {
+                return;
+            }
+            // If the bucket was forced, don't allow prediction to override
+            if (app.bucketingReason.equals(REASON_FORCED) && predicted) return;
             mAppIdleHistory.setAppStandbyBucket(packageName, userId, elapsedRealtime, newBucket,
                     reason);
         }
@@ -852,15 +866,19 @@
 
     void informListeners(String packageName, int userId, int bucket) {
         final boolean idle = bucket >= STANDBY_BUCKET_RARE;
-        for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
-            listener.onAppIdleStateChanged(packageName, userId, idle, bucket);
+        synchronized (mPackageAccessListeners) {
+            for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
+                listener.onAppIdleStateChanged(packageName, userId, idle, bucket);
+            }
         }
     }
 
     void informParoleStateChanged() {
         final boolean paroled = isParoledOrCharging();
-        for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
-            listener.onParoleStateChanged(paroled);
+        synchronized (mPackageAccessListeners) {
+            for (AppIdleStateChangeListener listener : mPackageAccessListeners) {
+                listener.onParoleStateChanged(paroled);
+            }
         }
     }
 
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 15284d5..07c860b 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -728,6 +728,10 @@
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
+            final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID;
+            final String reason = shellCaller
+                    ? UsageStatsManager.REASON_FORCED
+                    : UsageStatsManager.REASON_PREDICTED + ":" + callingUid;
             final long token = Binder.clearCallingIdentity();
             try {
                 // Caller cannot set their own standby state
@@ -735,8 +739,7 @@
                         PackageManager.MATCH_ANY_USER, userId) == callingUid) {
                     throw new IllegalArgumentException("Cannot set your own standby bucket");
                 }
-                mAppStandby.setAppStandbyBucket(packageName, userId, bucket,
-                        UsageStatsManager.REASON_PREDICTED + ":" + callingUid,
+                mAppStandby.setAppStandbyBucket(packageName, userId, bucket, reason,
                         SystemClock.elapsedRealtime());
             } finally {
                 Binder.restoreCallingIdentity(token);
@@ -779,6 +782,10 @@
             } catch (RemoteException re) {
                 throw re.rethrowFromSystemServer();
             }
+            final boolean shellCaller = callingUid == 0 || callingUid == Process.SHELL_UID;
+            final String reason = shellCaller
+                    ? UsageStatsManager.REASON_FORCED
+                    : UsageStatsManager.REASON_PREDICTED + ":" + callingUid;
             final long token = Binder.clearCallingIdentity();
             try {
                 final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -796,8 +803,7 @@
                             PackageManager.MATCH_ANY_USER, userId) == callingUid) {
                         throw new IllegalArgumentException("Cannot set your own standby bucket");
                     }
-                    mAppStandby.setAppStandbyBucket(packageName, userId, bucket,
-                            UsageStatsManager.REASON_PREDICTED + ":" + callingUid,
+                    mAppStandby.setAppStandbyBucket(packageName, userId, bucket, reason,
                             elapsedRealtime);
                 }
             } finally {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index ead8849..dedce17 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -300,10 +300,10 @@
      * If true all networks are considered as home network a.k.a non-roaming.  When false,
      * the 2 pairs of CMDA and GSM roaming/non-roaming arrays are consulted.
      *
-     * @see KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
-     * @see KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
-     * @see KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY
-     * @see KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY
+     * @see #KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
+     * @see #KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
+     * @see #KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY
+     * @see #KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY
      */
     public static final String
             KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
@@ -2059,7 +2059,7 @@
      * {@link android.service.carrier.CarrierService#onLoadConfig} will be called from an
      * arbitrary thread.
      * </p>
-     * @see #hasCarrierPrivileges
+     * @see TelephonyManager#hasCarrierPrivileges
      */
     public void notifyConfigChangedForSubId(int subId) {
         try {
diff --git a/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java
new file mode 100644
index 0000000..5d16dd5
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.internal;
+
+import android.os.RemoteException;
+import android.telephony.ims.internal.aidl.IImsCallSessionListener;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsConferenceState;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsStreamMediaProfile;
+import com.android.ims.ImsSuppServiceNotification;
+import com.android.ims.internal.ImsCallSession;
+
+/**
+ * Proxy class for interfacing with the framework's Call session for an ongoing IMS call.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsCallSessionListener maintained by other ImsServices.
+ *
+ * @hide
+ */
+public class ImsCallSessionListener {
+
+    private final IImsCallSessionListener mListener;
+
+    public ImsCallSessionListener(IImsCallSessionListener l) {
+        mListener = l;
+    }
+
+    /**
+     * Called when a request is sent out to initiate a new session
+     * and 1xx response is received from the network.
+     */
+    public void callSessionProgressing(ImsStreamMediaProfile profile)
+            throws RemoteException {
+        mListener.callSessionProgressing(profile);
+    }
+
+    /**
+     * Called when the session is initiated.
+     *
+     * @param profile the associated {@link ImsCallSession}.
+     */
+    public void callSessionInitiated(ImsCallProfile profile) throws RemoteException {
+        mListener.callSessionInitiated(profile);
+    }
+
+    /**
+     * Called when the session establishment has failed.
+     *
+     * @param reasonInfo detailed reason of the session establishment failure
+     */
+    public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) throws RemoteException {
+        mListener.callSessionInitiatedFailed(reasonInfo);
+    }
+
+    /**
+     * Called when the session is terminated.
+     *
+     * @param reasonInfo detailed reason of the session termination
+     */
+    public void callSessionTerminated(ImsReasonInfo reasonInfo) throws RemoteException {
+        mListener.callSessionTerminated(reasonInfo);
+    }
+
+    /**
+     * Called when the session is on hold.
+     */
+    public void callSessionHeld(ImsCallProfile profile) throws RemoteException {
+        mListener.callSessionHeld(profile);
+    }
+
+    /**
+     * Called when the session hold has failed.
+     *
+     * @param reasonInfo detailed reason of the session hold failure
+     */
+    public void callSessionHoldFailed(ImsReasonInfo reasonInfo) throws RemoteException {
+        mListener.callSessionHoldFailed(reasonInfo);
+    }
+
+    /**
+     * Called when the session hold is received from the remote user.
+     */
+    public void callSessionHoldReceived(ImsCallProfile profile) throws RemoteException {
+        mListener.callSessionHoldReceived(profile);
+    }
+
+    /**
+     * Called when the session resume is done.
+     */
+    public void callSessionResumed(ImsCallProfile profile) throws RemoteException {
+        mListener.callSessionResumed(profile);
+    }
+
+    /**
+     * Called when the session resume has failed.
+     *
+     * @param reasonInfo detailed reason of the session resume failure
+     */
+    public void callSessionResumeFailed(ImsReasonInfo reasonInfo) throws RemoteException {
+        mListener.callSessionResumeFailed(reasonInfo);
+    }
+
+    /**
+     * Called when the session resume is received from the remote user.
+     */
+    public void callSessionResumeReceived(ImsCallProfile profile) throws RemoteException {
+        mListener.callSessionResumeReceived(profile);
+    }
+
+    /**
+     * Called when the session merge has been started.  At this point, the {@code newSession}
+     * represents the session which has been initiated to the IMS conference server for the
+     * new merged conference.
+     *
+     * @param newSession the session object that is merged with an active & hold session
+     */
+    public void callSessionMergeStarted(ImsCallSession newSession, ImsCallProfile profile)
+            throws RemoteException {
+        mListener.callSessionMergeStarted(newSession != null ? newSession.getSession() : null,
+                profile);
+    }
+
+    /**
+     * Called when the session merge is successful and the merged session is active.
+     *
+     * @param newSession the new session object that is used for the conference
+     */
+    public void callSessionMergeComplete(ImsCallSession newSession) throws RemoteException {
+        mListener.callSessionMergeComplete(newSession != null ? newSession.getSession() : null);
+    }
+
+    /**
+     * Called when the session merge has failed.
+     *
+     * @param reasonInfo detailed reason of the call merge failure
+     */
+    public void callSessionMergeFailed(ImsReasonInfo reasonInfo) throws RemoteException {
+        mListener.callSessionMergeFailed(reasonInfo);
+    }
+
+    /**
+     * Called when the session is updated (except for hold/unhold).
+     */
+    public void callSessionUpdated(ImsCallProfile profile) throws RemoteException {
+        mListener.callSessionUpdated(profile);
+    }
+
+    /**
+     * Called when the session update has failed.
+     *
+     * @param reasonInfo detailed reason of the session update failure
+     */
+    public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) throws RemoteException {
+        mListener.callSessionUpdateFailed(reasonInfo);
+    }
+
+    /**
+     * Called when the session update is received from the remote user.
+     */
+    public void callSessionUpdateReceived(ImsCallProfile profile) throws RemoteException {
+        mListener.callSessionUpdateReceived(profile);
+    }
+
+    /**
+     * Called when the session has been extended to a conference session.
+     *
+     * @param newSession the session object that is extended to the conference
+     *      from the active session
+     */
+    public void callSessionConferenceExtended(ImsCallSession newSession, ImsCallProfile profile)
+            throws RemoteException {
+        mListener.callSessionConferenceExtended(newSession != null ? newSession.getSession() : null,
+                profile);
+    }
+
+    /**
+     * Called when the conference extension has failed.
+     *
+     * @param reasonInfo detailed reason of the conference extension failure
+     */
+    public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) throws RemoteException {
+        mListener.callSessionConferenceExtendFailed(reasonInfo);
+    }
+
+    /**
+     * Called when the conference extension is received from the remote user.
+     */
+    public void callSessionConferenceExtendReceived(ImsCallSession newSession,
+            ImsCallProfile profile) throws RemoteException {
+        mListener.callSessionConferenceExtendReceived(newSession != null
+                ? newSession.getSession() : null, profile);
+    }
+
+    /**
+     * Called when the invitation request of the participants is delivered to the conference
+     * server.
+     */
+    public void callSessionInviteParticipantsRequestDelivered() throws RemoteException {
+        mListener.callSessionInviteParticipantsRequestDelivered();
+    }
+
+    /**
+     * Called when the invitation request of the participants has failed.
+     *
+     * @param reasonInfo detailed reason of the conference invitation failure
+     */
+    public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo)
+            throws RemoteException {
+        mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
+    }
+
+    /**
+     * Called when the removal request of the participants is delivered to the conference
+     * server.
+     */
+    public void callSessionRemoveParticipantsRequestDelivered() throws RemoteException {
+        mListener.callSessionRemoveParticipantsRequestDelivered();
+    }
+
+    /**
+     * Called when the removal request of the participants has failed.
+     *
+     * @param reasonInfo detailed reason of the conference removal failure
+     */
+    public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo)
+            throws RemoteException {
+        mListener.callSessionInviteParticipantsRequestFailed(reasonInfo);
+    }
+
+    /**
+     * Notifies the framework of the updated Call session conference state.
+     *
+     * @param state the new {@link ImsConferenceState} associated with the conference.
+     */
+    public void callSessionConferenceStateUpdated(ImsConferenceState state) throws RemoteException {
+        mListener.callSessionConferenceStateUpdated(state);
+    }
+
+    /**
+     * Notifies the incoming USSD message.
+     */
+    public void callSessionUssdMessageReceived(int mode, String ussdMessage)
+            throws RemoteException {
+        mListener.callSessionUssdMessageReceived(mode, ussdMessage);
+    }
+
+    /**
+     * Notifies of a case where a {@link com.android.ims.internal.ImsCallSession} may potentially
+     * handover from one radio technology to another.
+     *
+     * @param srcAccessTech    The source radio access technology; one of the access technology
+     *                         constants defined in {@link android.telephony.ServiceState}.  For
+     *                         example
+     *                         {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+     * @param targetAccessTech The target radio access technology; one of the access technology
+     *                         constants defined in {@link android.telephony.ServiceState}.  For
+     *                         example
+     *                         {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}.
+     */
+    public void callSessionMayHandover(int srcAccessTech, int targetAccessTech)
+            throws RemoteException {
+        mListener.callSessionMayHandover(srcAccessTech, targetAccessTech);
+    }
+
+    /**
+     * Called when session access technology changes.
+     *
+     * @param srcAccessTech original access technology
+     * @param targetAccessTech new access technology
+     * @param reasonInfo
+     */
+    public void callSessionHandover(int srcAccessTech, int targetAccessTech,
+            ImsReasonInfo reasonInfo) throws RemoteException {
+        mListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo);
+    }
+
+    /**
+     * Called when session access technology change fails.
+     *
+     * @param srcAccessTech original access technology
+     * @param targetAccessTech new access technology
+     * @param reasonInfo handover failure reason
+     */
+    public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
+            ImsReasonInfo reasonInfo) throws RemoteException {
+        mListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo);
+    }
+
+    /**
+     * Called when the TTY mode is changed by the remote party.
+     *
+     * @param mode one of the following: -
+     *             {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} -
+     *             {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} -
+     *             {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} -
+     *             {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
+     */
+    public void callSessionTtyModeReceived(int mode) throws RemoteException {
+        mListener.callSessionTtyModeReceived(mode);
+    }
+
+    /**
+     * Called when the multiparty state is changed for this {@code ImsCallSession}.
+     *
+     * @param isMultiParty {@code true} if the session became multiparty,
+     *                     {@code false} otherwise.
+     */
+
+    public void callSessionMultipartyStateChanged(boolean isMultiParty) throws RemoteException {
+        mListener.callSessionMultipartyStateChanged(isMultiParty);
+    }
+
+    /**
+     * Called when the supplementary service information is received for the current session.
+     */
+    public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppSrvNotification)
+            throws RemoteException {
+        mListener.callSessionSuppServiceReceived(suppSrvNotification);
+    }
+
+    /**
+     * Received RTT modify request from the remote party.
+     *
+     * @param callProfile ImsCallProfile with updated attributes
+     */
+    public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile)
+            throws RemoteException {
+        mListener.callSessionRttModifyRequestReceived(callProfile);
+    }
+
+    /**
+     * @param status the received response for RTT modify request.
+     */
+    public void callSessionRttModifyResponseReceived(int status) throws RemoteException {
+        mListener.callSessionRttModifyResponseReceived(status);
+    }
+
+    /**
+     * Device received RTT message from Remote UE.
+     *
+     * @param rttMessage RTT message received
+     */
+    public void callSessionRttMessageReceived(String rttMessage) throws RemoteException {
+        mListener.callSessionRttMessageReceived(rttMessage);
+    }
+}
+
diff --git a/telephony/java/android/telephony/ims/internal/ImsService.java b/telephony/java/android/telephony/ims/internal/ImsService.java
new file mode 100644
index 0000000..b7c8ca0
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/ImsService.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.internal;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ims.internal.aidl.IImsConfig;
+import android.telephony.ims.internal.aidl.IImsMmTelFeature;
+import android.telephony.ims.internal.aidl.IImsRcsFeature;
+import android.telephony.ims.internal.aidl.IImsRegistration;
+import android.telephony.ims.internal.aidl.IImsServiceController;
+import android.telephony.ims.internal.aidl.IImsServiceControllerListener;
+import android.telephony.ims.internal.feature.ImsFeature;
+import android.telephony.ims.internal.feature.MmTelFeature;
+import android.telephony.ims.internal.feature.RcsFeature;
+import android.telephony.ims.internal.stub.ImsConfigImplBase;
+import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
+ * ImsService must register the service in their AndroidManifest to be detected by the framework.
+ * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE"
+ * permission. Then, the ImsService definition in the manifest must follow the following format:
+ *
+ * ...
+ * <service android:name=".EgImsService"
+ *     android:permission="android.permission.BIND_IMS_SERVICE" >
+ *     <!-- Apps must declare which features they support as metadata. The different categories are
+ *     defined below. In this example, the RCS_FEATURE feature is supported. -->
+ *     <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" />
+ *     <intent-filter>
+ *         <action android:name="android.telephony.ims.ImsService" />
+ *     </intent-filter>
+ * </service>
+ * ...
+ *
+ * The telephony framework will then bind to the ImsService you have defined in your manifest
+ * if you are either:
+ * 1) Defined as the default ImsService for the device in the device overlay using
+ *    "config_ims_package".
+ * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
+ *    {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}.
+ *
+ * The features that are currently supported in an ImsService are:
+ * - RCS_FEATURE: This ImsService implements the RcsFeature class.
+ * - MMTEL_FEATURE: This ImsService implements the MmTelFeature class.
+ *   @hide
+ */
+public class ImsService extends Service {
+
+    private static final String LOG_TAG = "ImsService";
+
+    /**
+     * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
+     * @hide
+     */
+    public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
+
+    // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that
+    // slot.
+    // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and
+    // call ImsFeature#onFeatureRemoved.
+    private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
+
+    private IImsServiceControllerListener mListener;
+
+
+    /**
+     * Listener that notifies the framework of ImsService changes.
+     */
+    public static class Listener extends IImsServiceControllerListener.Stub {
+        /**
+         * The IMS features that this ImsService supports has changed.
+         * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s
+         *   that this ImsService supports. This may trigger the addition/removal of feature
+         *   in this service.
+         */
+        public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) {
+        }
+    }
+
+    /**
+     * @hide
+     */
+    protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
+        @Override
+        public void setListener(IImsServiceControllerListener l) {
+            mListener = l;
+        }
+
+        @Override
+        public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) {
+            return createMmTelFeatureInternal(slotId, c);
+        }
+
+        @Override
+        public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) {
+            return createRcsFeatureInternal(slotId, c);
+        }
+
+        @Override
+        public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c)
+                throws RemoteException {
+            ImsService.this.removeImsFeature(slotId, featureType, c);
+        }
+
+        @Override
+        public ImsFeatureConfiguration querySupportedImsFeatures() {
+            return ImsService.this.querySupportedImsFeatures();
+        }
+
+        @Override
+        public void notifyImsServiceReadyForFeatureCreation() {
+            ImsService.this.readyForFeatureCreation();
+        }
+
+        @Override
+        public void notifyImsFeatureReady(int slotId, int featureType)
+                throws RemoteException {
+            ImsService.this.notifyImsFeatureReady(slotId, featureType);
+        }
+
+        @Override
+        public IImsConfig getConfig(int slotId) throws RemoteException {
+            ImsConfigImplBase c = ImsService.this.getConfig(slotId);
+            return c != null ? c.getBinder() : null;
+        }
+
+        @Override
+        public IImsRegistration getRegistration(int slotId) throws RemoteException {
+            ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
+            return r != null ? r.getBinder() : null;
+        }
+    };
+
+    /**
+     * @hide
+     */
+    @Override
+    public IBinder onBind(Intent intent) {
+        if(SERVICE_INTERFACE.equals(intent.getAction())) {
+            Log.i(LOG_TAG, "ImsService Bound.");
+            return mImsServiceController;
+        }
+        return null;
+    }
+
+    /**
+     * @hide
+     */
+    @VisibleForTesting
+    public SparseArray<ImsFeature> getFeatures(int slotId) {
+        return mFeaturesBySlot.get(slotId);
+    }
+
+    private IImsMmTelFeature createMmTelFeatureInternal(int slotId,
+            IImsFeatureStatusCallback c) {
+        MmTelFeature f = createMmTelFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c);
+            return f.getBinder();
+        } else {
+            Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
+            return null;
+        }
+    }
+
+    private IImsRcsFeature createRcsFeatureInternal(int slotId,
+            IImsFeatureStatusCallback c) {
+        RcsFeature f = createRcsFeature(slotId);
+        if (f != null) {
+            setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c);
+            return f.getBinder();
+        } else {
+            Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned.");
+            return null;
+        }
+    }
+
+    private void setupFeature(ImsFeature f, int slotId, int featureType,
+            IImsFeatureStatusCallback c) {
+        f.addImsFeatureStatusCallback(c);
+        f.initialize(this, slotId);
+        addImsFeature(slotId, featureType, f);
+    }
+
+    private void addImsFeature(int slotId, int featureType, ImsFeature f) {
+        synchronized (mFeaturesBySlot) {
+            // Get SparseArray for Features, by querying slot Id
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                // Populate new SparseArray of features if it doesn't exist for this slot yet.
+                features = new SparseArray<>();
+                mFeaturesBySlot.put(slotId, features);
+            }
+            features.put(featureType, f);
+        }
+    }
+
+    private void removeImsFeature(int slotId, int featureType,
+            IImsFeatureStatusCallback c) {
+        synchronized (mFeaturesBySlot) {
+            // get ImsFeature associated with the slot/feature
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot "
+                        + slotId);
+                return;
+            }
+            ImsFeature f = features.get(featureType);
+            if (f == null) {
+                Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type "
+                        + featureType + " exists on slot " + slotId);
+                return;
+            }
+            f.removeImsFeatureStatusCallback(c);
+            f.onFeatureRemoved();
+            features.remove(featureType);
+        }
+    }
+
+    private void notifyImsFeatureReady(int slotId, int featureType) {
+        synchronized (mFeaturesBySlot) {
+            // get ImsFeature associated with the slot/feature
+            SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId);
+            if (features == null) {
+                Log.w(LOG_TAG, "Can not notify ImsFeature ready. No ImsFeatures exist on " +
+                        "slot " + slotId);
+                return;
+            }
+            ImsFeature f = features.get(featureType);
+            if (f == null) {
+                Log.w(LOG_TAG, "Can not notify ImsFeature ready. No feature with type "
+                        + featureType + " exists on slot " + slotId);
+                return;
+            }
+            f.onFeatureReady();
+        }
+    }
+
+    /**
+     * When called, provide the {@link ImsFeatureConfiguration} that this ImsService currently
+     * supports. This will trigger the framework to set up the {@link ImsFeature}s that correspond
+     * to the {@link ImsFeature.FeatureType}s configured here.
+     * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports,
+     * defined in {@link ImsFeature.FeatureType}.
+     */
+    public ImsFeatureConfiguration querySupportedImsFeatures() {
+        // Return empty for base implementation
+        return new ImsFeatureConfiguration();
+    }
+
+    /**
+     * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated
+     * features, defined in {@link ImsFeature.FeatureType} that this ImsService supports. This may
+     * trigger the framework to add/remove new ImsFeatures, depending on the configuration.
+     */
+    public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c)
+            throws RemoteException {
+        if (mListener == null) {
+            throw new IllegalStateException("Framework is not ready");
+        }
+        mListener.onUpdateSupportedImsFeatures(c);
+    }
+
+    /**
+     * The ImsService has been bound and is ready for ImsFeature creation based on the Features that
+     * the ImsService has registered for with the framework, either in the manifest or via
+     * The ImsService should use this signal instead of onCreate/onBind or similar to perform
+     * feature initialization because the framework may bind to this service multiple times to
+     * query the ImsService's {@link ImsFeatureConfiguration} via
+     * {@link #querySupportedImsFeatures()}before creating features.
+     */
+    public void readyForFeatureCreation() {
+    }
+
+    /**
+     * When called, the framework is requesting that a new MmTelFeature is created for the specified
+     * slot.
+     *
+     * @param slotId The slot ID that the MMTel Feature is being created for.
+     * @return The newly created MmTelFeature associated with the slot or null if the feature is not
+     * supported.
+     */
+    public MmTelFeature createMmTelFeature(int slotId) {
+        return null;
+    }
+
+    /**
+     * When called, the framework is requesting that a new RcsFeature is created for the specified
+     * slot
+     *
+     * @param slotId The slot ID that the RCS Feature is being created for.
+     * @return The newly created RcsFeature associated with the slot or null if the feature is not
+     * supported.
+     */
+    public RcsFeature createRcsFeature(int slotId) {
+        return null;
+    }
+
+    /**
+     * @param slotId The slot that the IMS configuration is associated with.
+     * @return ImsConfig implementation that is associated with the specified slot.
+     */
+    public ImsConfigImplBase getConfig(int slotId) {
+        return new ImsConfigImplBase();
+    }
+
+    /**
+     * @param slotId The slot that is associated with the IMS Registration.
+     * @return the ImsRegistration implementation associated with the slot.
+     */
+    public ImsRegistrationImplBase getRegistration(int slotId) {
+        return new ImsRegistrationImplBase();
+    }
+}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl
new file mode 100644
index 0000000..2fb6744
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2013 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.internal.aidl;
+
+import com.android.ims.ImsStreamMediaProfile;
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsConferenceState;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.ImsSuppServiceNotification;
+
+/**
+ * A listener type for receiving notification on IMS call session events.
+ * When an event is generated for an {@link IImsCallSession}, the application is notified
+ * by having one of the methods called on the {@link IImsCallSessionListener}.
+ * {@hide}
+ */
+oneway interface IImsCallSessionListener {
+    /**
+     * Notifies the result of the basic session operation (setup / terminate).
+     */
+    void callSessionProgressing(in ImsStreamMediaProfile profile);
+    void callSessionInitiated(in ImsCallProfile profile);
+    void callSessionInitiatedFailed(in ImsReasonInfo reasonInfo);
+    void callSessionTerminated(in ImsReasonInfo reasonInfo);
+
+    /**
+     * Notifies the result of the call hold/resume operation.
+     */
+    void callSessionHeld(in ImsCallProfile profile);
+    void callSessionHoldFailed(in ImsReasonInfo reasonInfo);
+    void callSessionHoldReceived(in ImsCallProfile profile);
+    void callSessionResumed(in ImsCallProfile profile);
+    void callSessionResumeFailed(in ImsReasonInfo reasonInfo);
+    void callSessionResumeReceived(in ImsCallProfile profile);
+
+    /**
+     * Notifies the result of call merge operation.
+     */
+    void callSessionMergeStarted(IImsCallSession newSession, in ImsCallProfile profile);
+    void callSessionMergeComplete(IImsCallSession session);
+    void callSessionMergeFailed(in ImsReasonInfo reasonInfo);
+
+    /**
+     * Notifies the result of call upgrade / downgrade or any other call updates.
+     */
+    void callSessionUpdated(in ImsCallProfile profile);
+    void callSessionUpdateFailed(in ImsReasonInfo reasonInfo);
+    void callSessionUpdateReceived(in ImsCallProfile profile);
+
+    /**
+     * Notifies the result of conference extension.
+     */
+    void callSessionConferenceExtended(IImsCallSession newSession, in ImsCallProfile profile);
+    void callSessionConferenceExtendFailed(in ImsReasonInfo reasonInfo);
+    void callSessionConferenceExtendReceived(IImsCallSession newSession,
+            in ImsCallProfile profile);
+
+    /**
+     * Notifies the result of the participant invitation / removal to/from the conference session.
+     */
+    void callSessionInviteParticipantsRequestDelivered();
+    void callSessionInviteParticipantsRequestFailed(in ImsReasonInfo reasonInfo);
+    void callSessionRemoveParticipantsRequestDelivered();
+    void callSessionRemoveParticipantsRequestFailed(in ImsReasonInfo reasonInfo);
+
+    /**
+     * Notifies the changes of the conference info. in the conference session.
+     */
+    void callSessionConferenceStateUpdated(in ImsConferenceState state);
+
+    /**
+     * Notifies the incoming USSD message.
+     */
+    void callSessionUssdMessageReceived(int mode, String ussdMessage);
+
+    /**
+     * Notifies of handover information for this call
+     */
+    void callSessionHandover(int srcAccessTech, int targetAccessTech,
+            in ImsReasonInfo reasonInfo);
+    void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech,
+            in ImsReasonInfo reasonInfo);
+    void callSessionMayHandover(int srcAccessTech, int targetAccessTech);
+
+    /**
+     * Notifies the TTY mode change by remote party.
+     * @param mode one of the following:
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_OFF}
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_FULL}
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_HCO}
+     * - {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
+     */
+    void callSessionTtyModeReceived(int mode);
+
+    /**
+     * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
+     *
+     * @param session The call session.
+     * @param isMultiParty {@code true} if the session became multiparty, {@code false} otherwise.
+     */
+    void callSessionMultipartyStateChanged(boolean isMultiParty);
+
+    /**
+     * Notifies the supplementary service information for the current session.
+     */
+    void callSessionSuppServiceReceived(in ImsSuppServiceNotification suppSrvNotification);
+
+    /**
+     * Device received RTT modify request from Remote UE
+     * @param session ImsCallProfile with updated attribute
+     */
+    void callSessionRttModifyRequestReceived(in ImsCallProfile callProfile);
+
+    /* Device issued RTT modify request and inturn received response
+     * from Remote UE
+     * @param status Will be one of the following values from:
+     * - {@link Connection.RttModifyStatus}
+     */
+    void callSessionRttModifyResponseReceived(int status);
+
+    /*
+     * While in call, device received RTT message from Remote UE
+     * @param rttMessage Received RTT message
+     */
+    void callSessionRttMessageReceived(in String rttMessage);
+}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl
new file mode 100644
index 0000000..fd2eb24
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.internal.aidl;
+
+/**
+ * See ImsFeature#CapabilityCallback for more information.
+ * {@hide}
+ */
+oneway interface IImsCapabilityCallback {
+    void onQueryCapabilityConfiguration(int capability, int radioTech, boolean enabled);
+    void onChangeCapabilityConfigurationError(int capability, int radioTech, int reason);
+    void onCapabilitiesStatusChanged(int config);
+}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl
new file mode 100644
index 0000000..3d424a3
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2013 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.internal.aidl;
+
+import android.telephony.ims.internal.aidl.IImsConfigCallback;
+
+import com.android.ims.ImsConfigListener;
+
+/**
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include items provisioned by the operator.
+ *
+ * {@hide}
+ */
+interface IImsConfig {
+
+    void addImsConfigCallback(IImsConfigCallback c);
+    void removeImsConfigCallback(IImsConfigCallback c);
+    int getConfigInt(int item);
+    String getConfigString(int item);
+    // Return result code defined in ImsConfig#OperationStatusConstants
+    int setConfigInt(int item, int value);
+    // Return result code defined in ImsConfig#OperationStatusConstants
+    int setConfigString(int item, String value);
+}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl
new file mode 100644
index 0000000..52efd23
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2013 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.internal.aidl;
+
+/**
+ * Provides callback interface for ImsConfig when a value has changed.
+ *
+ * {@hide}
+ */
+oneway interface IImsConfigCallback {
+    void onIntConfigChanged(int item, int value);
+    void onStringConfigChanged(int item, String value);
+}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl
new file mode 100644
index 0000000..7125781
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.internal.aidl;
+
+import android.os.Message;
+import android.telephony.ims.internal.aidl.IImsMmTelListener;
+import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
+import android.telephony.ims.internal.aidl.IImsCallSessionListener;
+import android.telephony.ims.internal.feature.CapabilityChangeRequest;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsUt;
+
+/**
+ * See MmTelFeature for more information.
+ * {@hide}
+ */
+interface IImsMmTelFeature {
+    void setListener(IImsMmTelListener l);
+    int getFeatureState();
+    ImsCallProfile createCallProfile(int callSessionType, int callType);
+    IImsCallSession createCallSession(in ImsCallProfile profile, IImsCallSessionListener listener);
+    IImsUt getUtInterface();
+    IImsEcbm getEcbmInterface();
+    void setUiTtyMode(int uiTtyMode, in Message onCompleteMessage);
+    IImsMultiEndpoint getMultiEndpointInterface();
+    int queryCapabilityStatus();
+    oneway void addCapabilityCallback(IImsCapabilityCallback c);
+    oneway void removeCapabilityCallback(IImsCapabilityCallback c);
+    oneway void changeCapabilitiesConfiguration(in CapabilityChangeRequest request,
+            IImsCapabilityCallback c);
+    oneway void queryCapabilityConfiguration(int capability, int radioTech,
+            IImsCapabilityCallback c);
+}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
new file mode 100644
index 0000000..8332bc0
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.internal.aidl;
+
+import com.android.ims.internal.IImsCallSession;
+
+/**
+ * See MmTelFeature#Listener for more information.
+ * {@hide}
+ */
+oneway interface IImsMmTelListener {
+    void onIncomingCall(IImsCallSession c);
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl
new file mode 100644
index 0000000..f6005b6
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.internal.aidl;
+
+/**
+ * See RcsFeature for more information.
+ * {@hide}
+ */
+interface IImsRcsFeature {
+    //Empty Default Implementation
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl
new file mode 100644
index 0000000..687b7ca
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013 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.internal.aidl;
+
+import android.telephony.ims.internal.aidl.IImsRegistrationCallback;
+import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+
+/**
+ * See ImsRegistration for more information.
+ *
+ * {@hide}
+ */
+interface IImsRegistration {
+   int getRegistrationTechnology();
+   oneway void addRegistrationCallback(IImsRegistrationCallback c);
+   oneway void removeRegistrationCallback(IImsRegistrationCallback c);
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl
new file mode 100644
index 0000000..a50575b
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2013 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.internal.aidl;
+
+import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+
+import com.android.ims.ImsReasonInfo;
+
+/**
+ * See ImsRegistrationImplBase.Callback for more information.
+ *
+ * {@hide}
+ */
+oneway interface IImsRegistrationCallback {
+   void onRegistered(int imsRadioTech);
+   void onRegistering(int imsRadioTech);
+   void onDeregistered(in ImsReasonInfo info);
+   void onTechnologyChangeFailed(int imsRadioTech, in ImsReasonInfo info);
+}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
new file mode 100644
index 0000000..8afb955
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.internal.aidl;
+
+import android.telephony.ims.internal.aidl.IImsMmTelFeature;
+import android.telephony.ims.internal.aidl.IImsRcsFeature;
+import android.telephony.ims.internal.aidl.IImsRegistration;
+import android.telephony.ims.internal.aidl.IImsConfig;
+import android.telephony.ims.internal.aidl.IImsServiceControllerListener;
+import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+
+/**
+ * See ImsService and MmTelFeature for more information.
+ * {@hide}
+ */
+interface IImsServiceController {
+    void setListener(IImsServiceControllerListener l);
+    IImsMmTelFeature createMmTelFeature(int slotId, in IImsFeatureStatusCallback c);
+    IImsRcsFeature createRcsFeature(int slotId, in IImsFeatureStatusCallback c);
+    ImsFeatureConfiguration querySupportedImsFeatures();
+    // Synchronous call to ensure the ImsService is ready before continuing with feature creation.
+    void notifyImsServiceReadyForFeatureCreation();
+    // Synchronous call to ensure the new ImsFeature is ready before using the Feature.
+    void notifyImsFeatureReady(int slotId, int featureType);
+    void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c);
+    IImsConfig getConfig(int slotId);
+    IImsRegistration getRegistration(int slotId);
+}
diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl
new file mode 100644
index 0000000..01cca2db
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims.internal.aidl;
+
+import android.telephony.ims.internal.stub.ImsFeatureConfiguration;
+
+/**
+ * See ImsService#Listener for more information.
+ * {@hide}
+ */
+oneway interface IImsServiceControllerListener {
+    void onUpdateSupportedImsFeatures(in ImsFeatureConfiguration c);
+}
diff --git a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl
new file mode 100644
index 0000000..f4ec0eb
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.internal.feature;
+
+parcelable CapabilityChangeRequest;
diff --git a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
new file mode 100644
index 0000000..4d18873
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.internal.feature;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
+import android.util.ArraySet;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Request to send to IMS provider, which will try to enable/disable capabilities that are added to
+ * the request.
+ * {@hide}
+ */
+public class CapabilityChangeRequest implements Parcelable {
+
+    public static class CapabilityPair {
+        private final int mCapability;
+        private final int radioTech;
+
+        public CapabilityPair(int capability, int radioTech) {
+            this.mCapability = capability;
+            this.radioTech = radioTech;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof CapabilityPair)) return false;
+
+            CapabilityPair that = (CapabilityPair) o;
+
+            if (getCapability() != that.getCapability()) return false;
+            return getRadioTech() == that.getRadioTech();
+        }
+
+        @Override
+        public int hashCode() {
+            int result = getCapability();
+            result = 31 * result + getRadioTech();
+            return result;
+        }
+
+        public @MmTelFeature.MmTelCapabilities.MmTelCapability int getCapability() {
+            return mCapability;
+        }
+
+        public @ImsRegistrationImplBase.ImsRegistrationTech int getRadioTech() {
+            return radioTech;
+        }
+    }
+
+    // Pair contains <radio tech, mCapability>
+    private final Set<CapabilityPair> mCapabilitiesToEnable;
+    // Pair contains <radio tech, mCapability>
+    private final Set<CapabilityPair> mCapabilitiesToDisable;
+
+    public CapabilityChangeRequest() {
+        mCapabilitiesToEnable = new ArraySet<>();
+        mCapabilitiesToDisable = new ArraySet<>();
+    }
+
+    /**
+     * Add one or many capabilities to the request to be enabled.
+     *
+     * @param capabilities A bitfield of capabilities to enable, valid values are defined in
+     *   {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
+     * @param radioTech  the radio tech that these capabilities should be enabled for, valid
+     *   values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+     */
+    public void addCapabilitiesToEnableForTech(
+            @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+        addAllCapabilities(mCapabilitiesToEnable, capabilities, radioTech);
+    }
+
+    /**
+     * Add one or many capabilities to the request to be disabled.
+     * @param capabilities A bitfield of capabilities to diable, valid values are defined in
+     *   {@link MmTelFeature.MmTelCapabilities.MmTelCapability}.
+     * @param radioTech the radio tech that these capabilities should be disabled for, valid
+     *   values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}.
+     */
+    public void addCapabilitiesToDisableForTech(
+            @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+        addAllCapabilities(mCapabilitiesToDisable, capabilities, radioTech);
+    }
+
+    /**
+     * @return a {@link List} of {@link CapabilityPair}s that are requesting to be enabled.
+     */
+    public List<CapabilityPair> getCapabilitiesToEnable() {
+        return new ArrayList<>(mCapabilitiesToEnable);
+    }
+
+    /**
+     * @return a {@link List} of {@link CapabilityPair}s that are requesting to be disabled.
+     */
+    public List<CapabilityPair> getCapabilitiesToDisable() {
+        return new ArrayList<>(mCapabilitiesToDisable);
+    }
+
+    // Iterate through capabilities bitfield and add each one as a pair associated with the radio
+    // technology
+    private void addAllCapabilities(Set<CapabilityPair> set, int capabilities, int tech) {
+        long highestCapability = Long.highestOneBit(capabilities);
+        for (int i = 1; i <= highestCapability; i *= 2) {
+            if ((i & capabilities) > 0) {
+                set.add(new CapabilityPair(/*capability*/ i, /*radioTech*/ tech));
+            }
+        }
+    }
+
+    protected CapabilityChangeRequest(Parcel in) {
+        int enableSize = in.readInt();
+        mCapabilitiesToEnable = new ArraySet<>(enableSize);
+        for (int i = 0; i < enableSize; i++) {
+            mCapabilitiesToEnable.add(new CapabilityPair(/*capability*/ in.readInt(),
+                    /*radioTech*/ in.readInt()));
+        }
+        int disableSize = in.readInt();
+        mCapabilitiesToDisable = new ArraySet<>(disableSize);
+        for (int i = 0; i < disableSize; i++) {
+            mCapabilitiesToDisable.add(new CapabilityPair(/*capability*/ in.readInt(),
+                    /*radioTech*/ in.readInt()));
+        }
+    }
+
+    public static final Creator<CapabilityChangeRequest> CREATOR =
+            new Creator<CapabilityChangeRequest>() {
+                @Override
+                public CapabilityChangeRequest createFromParcel(Parcel in) {
+                    return new CapabilityChangeRequest(in);
+                }
+
+                @Override
+                public CapabilityChangeRequest[] newArray(int size) {
+                    return new CapabilityChangeRequest[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mCapabilitiesToEnable.size());
+        for (CapabilityPair pair : mCapabilitiesToEnable) {
+            dest.writeInt(pair.getCapability());
+            dest.writeInt(pair.getRadioTech());
+        }
+        dest.writeInt(mCapabilitiesToDisable.size());
+        for (CapabilityPair pair : mCapabilitiesToDisable) {
+            dest.writeInt(pair.getCapability());
+            dest.writeInt(pair.getRadioTech());
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof CapabilityChangeRequest)) return false;
+
+        CapabilityChangeRequest that = (CapabilityChangeRequest) o;
+
+        if (!mCapabilitiesToEnable.equals(that.mCapabilitiesToEnable)) return false;
+        return mCapabilitiesToDisable.equals(that.mCapabilitiesToDisable);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = mCapabilitiesToEnable.hashCode();
+        result = 31 * result + mCapabilitiesToDisable.hashCode();
+        return result;
+    }
+}
diff --git a/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java b/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java
new file mode 100644
index 0000000..9f82ad2
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.internal.feature;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IInterface;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.telephony.SubscriptionManager;
+import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
+import android.util.Log;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+/**
+ * Base class for all IMS features that are supported by the framework.
+ *
+ * @hide
+ */
+public abstract class ImsFeature {
+
+    private static final String LOG_TAG = "ImsFeature";
+
+    /**
+     * Action to broadcast when ImsService is up.
+     * Internal use only.
+     * Only defined here separately for compatibility purposes with the old ImsService.
+     *
+     * @hide
+     */
+    public static final String ACTION_IMS_SERVICE_UP =
+            "com.android.ims.IMS_SERVICE_UP";
+
+    /**
+     * Action to broadcast when ImsService is down.
+     * Internal use only.
+     * Only defined here separately for compatibility purposes with the old ImsService.
+     *
+     * @hide
+     */
+    public static final String ACTION_IMS_SERVICE_DOWN =
+            "com.android.ims.IMS_SERVICE_DOWN";
+
+    /**
+     * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
+     * A long value; the phone ID corresponding to the IMS service coming up or down.
+     * Only defined here separately for compatibility purposes with the old ImsService.
+     *
+     * @hide
+     */
+    public static final String EXTRA_PHONE_ID = "android:phone_id";
+
+    // Invalid feature value
+    public static final int FEATURE_INVALID = -1;
+    // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
+    // defined values in ImsServiceClass for compatibility purposes.
+    public static final int FEATURE_EMERGENCY_MMTEL = 0;
+    public static final int FEATURE_MMTEL = 1;
+    public static final int FEATURE_RCS = 2;
+    // Total number of features defined
+    public static final int FEATURE_MAX = 3;
+
+    // Integer values defining IMS features that are supported in ImsFeature.
+    @IntDef(flag = true,
+            value = {
+                    FEATURE_EMERGENCY_MMTEL,
+                    FEATURE_MMTEL,
+                    FEATURE_RCS
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface FeatureType {}
+
+    // Integer values defining the state of the ImsFeature at any time.
+    @IntDef(flag = true,
+            value = {
+                    STATE_UNAVAILABLE,
+                    STATE_INITIALIZING,
+                    STATE_READY,
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsState {}
+
+    public static final int STATE_UNAVAILABLE = 0;
+    public static final int STATE_INITIALIZING = 1;
+    public static final int STATE_READY = 2;
+
+    // Integer values defining the result codes that should be returned from
+    // {@link changeEnabledCapabilities} when the framework tries to set a feature's capability.
+    @IntDef(flag = true,
+            value = {
+                    CAPABILITY_ERROR_GENERIC,
+                    CAPABILITY_SUCCESS
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsCapabilityError {}
+
+    public static final int CAPABILITY_ERROR_GENERIC = -1;
+    public static final int CAPABILITY_SUCCESS = 0;
+
+
+    /**
+     * The framework implements this callback in order to register for Feature Capability status
+     * updates, via {@link #onCapabilitiesStatusChanged(Capabilities)}, query Capability
+     * configurations, via {@link #onQueryCapabilityConfiguration}, as well as to receive error
+     * callbacks when the ImsService can not change the capability as requested, via
+     * {@link #onChangeCapabilityConfigurationError}.
+     */
+    public static class CapabilityCallback extends IImsCapabilityCallback.Stub {
+
+        @Override
+        public final void onCapabilitiesStatusChanged(int config) throws RemoteException {
+            onCapabilitiesStatusChanged(new Capabilities(config));
+        }
+
+        /**
+         * Returns the result of a query for the capability configuration of a requested capability.
+         *
+         * @param capability The capability that was requested.
+         * @param radioTech The IMS radio technology associated with the capability.
+         * @param isEnabled true if the capability is enabled, false otherwise.
+         */
+        @Override
+        public void onQueryCapabilityConfiguration(int capability, int radioTech,
+                boolean isEnabled) {
+
+        }
+
+        /**
+         * Called when a change to the capability configuration has returned an error.
+         *
+         * @param capability The capability that was requested to be changed.
+         * @param radioTech The IMS radio technology associated with the capability.
+         * @param reason error associated with the failure to change configuration.
+         */
+        @Override
+        public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+                int reason) {
+        }
+
+        /**
+         * The status of the feature's capabilities has changed to either available or unavailable.
+         * If unavailable, the feature is not able to support the unavailable capability at this
+         * time.
+         *
+         * @param config The new availability of the capabilities.
+         */
+        public void onCapabilitiesStatusChanged(Capabilities config) {
+        }
+    }
+
+    /**
+     * Used by the ImsFeature to call back to the CapabilityCallback that the framework has
+     * provided.
+     */
+    protected static class CapabilityCallbackProxy {
+        private final IImsCapabilityCallback mCallback;
+
+        public CapabilityCallbackProxy(IImsCapabilityCallback c) {
+            mCallback = c;
+        }
+
+        /**
+         * This method notifies the provided framework callback that the request to change the
+         * indicated capability has failed and has not changed.
+         *
+         * @param capability The Capability that will be notified to the framework.
+         * @param radioTech The radio tech that this capability failed for.
+         * @param reason The reason this capability was unable to be changed.
+         */
+        public void onChangeCapabilityConfigurationError(int capability, int radioTech,
+                @ImsCapabilityError int reason) {
+            try {
+                mCallback.onChangeCapabilityConfigurationError(capability, radioTech, reason);
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "onChangeCapabilityConfigurationError called on dead binder.");
+            }
+        }
+
+        public void onQueryCapabilityConfiguration(int capability, int radioTech,
+                boolean isEnabled) {
+            try {
+                mCallback.onQueryCapabilityConfiguration(capability, radioTech, isEnabled);
+            } catch (RemoteException e) {
+                Log.e(LOG_TAG, "onQueryCapabilityConfiguration called on dead binder.");
+            }
+        }
+    }
+
+    /**
+     * Contains the capabilities defined and supported by an ImsFeature in the form of a bit mask.
+     */
+    public static class Capabilities {
+        protected int mCapabilities = 0;
+
+        public Capabilities() {
+        }
+
+        protected Capabilities(int capabilities) {
+            mCapabilities = capabilities;
+        }
+
+        /**
+         * @param capabilities Capabilities to be added to the configuration in the form of a
+         *     bit mask.
+         */
+        public void addCapabilities(int capabilities) {
+            mCapabilities |= capabilities;
+        }
+
+        /**
+         * @param capabilities Capabilities to be removed to the configuration in the form of a
+         *     bit mask.
+         */
+        public void removeCapabilities(int capabilities) {
+            mCapabilities &= ~capabilities;
+        }
+
+        /**
+         * @return true if all of the capabilities specified are capable.
+         */
+        public boolean isCapable(int capabilities) {
+            return (mCapabilities & capabilities) == capabilities;
+        }
+
+        public Capabilities copy() {
+            return new Capabilities(mCapabilities);
+        }
+
+        /**
+         * @return a bitmask containing the capability flags directly.
+         */
+        public int getMask() {
+            return mCapabilities;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof Capabilities)) return false;
+
+            Capabilities that = (Capabilities) o;
+
+            return mCapabilities == that.mCapabilities;
+        }
+
+        @Override
+        public int hashCode() {
+            return mCapabilities;
+        }
+    }
+
+    private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap(
+            new WeakHashMap<IImsFeatureStatusCallback, Boolean>());
+    private @ImsState int mState = STATE_UNAVAILABLE;
+    private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+    private Context mContext;
+    private final Object mLock = new Object();
+    private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks
+            = new RemoteCallbackList<>();
+    private Capabilities mCapabilityStatus = new Capabilities();
+
+    public final void initialize(Context context, int slotId) {
+        mContext = context;
+        mSlotId = slotId;
+    }
+
+    public final int getFeatureState() {
+        synchronized (mLock) {
+            return mState;
+        }
+    }
+
+    protected final void setFeatureState(@ImsState int state) {
+        synchronized (mLock) {
+            if (mState != state) {
+                mState = state;
+                notifyFeatureState(state);
+            }
+        }
+    }
+
+    // Not final for testing, but shouldn't be extended!
+    @VisibleForTesting
+    public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
+        try {
+            // If we have just connected, send queued status.
+            c.notifyImsFeatureStatus(getFeatureState());
+            // Add the callback if the callback completes successfully without a RemoteException.
+            synchronized (mLock) {
+                mStatusCallbacks.add(c);
+            }
+        } catch (RemoteException e) {
+            Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+        }
+    }
+
+    @VisibleForTesting
+    // Not final for testing, but should not be extended!
+    public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) {
+        synchronized (mLock) {
+            mStatusCallbacks.remove(c);
+        }
+    }
+
+    /**
+     * Internal method called by ImsFeature when setFeatureState has changed.
+     */
+    private void notifyFeatureState(@ImsState int state) {
+        synchronized (mLock) {
+            for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator();
+                    iter.hasNext(); ) {
+                IImsFeatureStatusCallback callback = iter.next();
+                try {
+                    Log.i(LOG_TAG, "notifying ImsFeatureState=" + state);
+                    callback.notifyImsFeatureStatus(state);
+                } catch (RemoteException e) {
+                    // remove if the callback is no longer alive.
+                    iter.remove();
+                    Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+                }
+            }
+        }
+        sendImsServiceIntent(state);
+    }
+
+    /**
+     * Provide backwards compatibility using deprecated service UP/DOWN intents.
+     */
+    private void sendImsServiceIntent(@ImsState int state) {
+        if (mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+            return;
+        }
+        Intent intent;
+        switch (state) {
+            case ImsFeature.STATE_UNAVAILABLE:
+            case ImsFeature.STATE_INITIALIZING:
+                intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+                break;
+            case ImsFeature.STATE_READY:
+                intent = new Intent(ACTION_IMS_SERVICE_UP);
+                break;
+            default:
+                intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+        }
+        intent.putExtra(EXTRA_PHONE_ID, mSlotId);
+        mContext.sendBroadcast(intent);
+    }
+
+    public final void addCapabilityCallback(IImsCapabilityCallback c) {
+        mCapabilityCallbacks.register(c);
+    }
+
+    public final void removeCapabilityCallback(IImsCapabilityCallback c) {
+        mCapabilityCallbacks.unregister(c);
+    }
+
+    /**
+     * @return the cached capabilities status for this feature.
+     */
+    @VisibleForTesting
+    public Capabilities queryCapabilityStatus() {
+        synchronized (mLock) {
+            return mCapabilityStatus.copy();
+        }
+    }
+
+    // Called internally to request the change of enabled capabilities.
+    @VisibleForTesting
+    public final void requestChangeEnabledCapabilities(CapabilityChangeRequest request,
+            IImsCapabilityCallback c) throws RemoteException {
+        if (request == null) {
+            throw new IllegalArgumentException(
+                    "ImsFeature#requestChangeEnabledCapabilities called with invalid params.");
+        }
+        changeEnabledCapabilities(request, new CapabilityCallbackProxy(c));
+    }
+
+    /**
+     * Called by the ImsFeature when the capabilities status has changed.
+     *
+     * @param c A {@link Capabilities} containing the new Capabilities status.
+     */
+    protected final void notifyCapabilitiesStatusChanged(Capabilities c) {
+        synchronized (mLock) {
+            mCapabilityStatus = c.copy();
+        }
+        int count = mCapabilityCallbacks.beginBroadcast();
+        try {
+            for (int i = 0; i < count; i++) {
+                try {
+                    mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged(
+                            c.mCapabilities);
+                } catch (RemoteException e) {
+                    Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " +
+                            "callback.");
+                }
+            }
+        } finally {
+            mCapabilityCallbacks.finishBroadcast();
+        }
+    }
+
+    /**
+     * Features should override this method to receive Capability preference change requests from
+     * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities
+     * in the {@link CapabilityChangeRequest} are not able to be completed due to an error,
+     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for
+     * each failed capability.
+     *
+     * @param request A {@link CapabilityChangeRequest} containing requested capabilities to
+     *     enable/disable.
+     * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework
+     * setting a subset of these capabilities fail, using
+     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}.
+     */
+    public abstract void changeEnabledCapabilities(CapabilityChangeRequest request,
+            CapabilityCallbackProxy c);
+
+    /**
+     * Called when the framework is removing this feature and it needs to be cleaned up.
+     */
+    public abstract void onFeatureRemoved();
+
+    /**
+     * Called when the feature has been initialized and communication with the framework is set up.
+     * Any attempt by this feature to access the framework before this method is called will return
+     * with an {@link IllegalStateException}.
+     * The IMS provider should use this method to trigger registration for this feature on the IMS
+     * network, if needed.
+     */
+    public abstract void onFeatureReady();
+
+    /**
+     * @return Binder instance that the framework will use to communicate with this feature.
+     */
+    protected abstract IInterface getBinder();
+}
diff --git a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
new file mode 100644
index 0000000..f183a57
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.internal.feature;
+
+import android.annotation.IntDef;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telecom.TelecomManager;
+import android.telephony.ims.internal.ImsCallSessionListener;
+import android.telephony.ims.internal.aidl.IImsCallSessionListener;
+import android.telephony.ims.internal.aidl.IImsCapabilityCallback;
+import android.telephony.ims.internal.aidl.IImsMmTelFeature;
+import android.telephony.ims.internal.aidl.IImsMmTelListener;
+import android.telephony.ims.internal.stub.ImsRegistrationImplBase;
+import android.telephony.ims.stub.ImsEcbmImplBase;
+import android.telephony.ims.stub.ImsMultiEndpointImplBase;
+import android.telephony.ims.stub.ImsUtImplBase;
+import android.util.Log;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsUt;
+import com.android.ims.internal.ImsCallSession;
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
+ *
+ * Any class wishing to use MmTelFeature should extend this class and implement all methods that the
+ * service supports.
+ * @hide
+ */
+
+public class MmTelFeature extends ImsFeature {
+
+    private static final String LOG_TAG = "MmTelFeature";
+
+    private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
+
+        @Override
+        public void setListener(IImsMmTelListener l) throws RemoteException {
+            synchronized (mLock) {
+                MmTelFeature.this.setListener(l);
+            }
+        }
+
+        @Override
+        public int getFeatureState() throws RemoteException {
+            synchronized (mLock) {
+                return MmTelFeature.this.getFeatureState();
+            }
+        }
+
+
+        @Override
+        public ImsCallProfile createCallProfile(int callSessionType, int callType)
+                throws RemoteException {
+            synchronized (mLock) {
+                return MmTelFeature.this.createCallProfile(callSessionType,  callType);
+            }
+        }
+
+        @Override
+        public IImsCallSession createCallSession(ImsCallProfile profile,
+                IImsCallSessionListener listener) throws RemoteException {
+            synchronized (mLock) {
+                ImsCallSession s = MmTelFeature.this.createCallSession(profile,
+                        new ImsCallSessionListener(listener));
+                return s != null ? s.getSession() : null;
+            }
+        }
+
+        @Override
+        public IImsUt getUtInterface() throws RemoteException {
+            synchronized (mLock) {
+                return MmTelFeature.this.getUt();
+            }
+        }
+
+        @Override
+        public IImsEcbm getEcbmInterface() throws RemoteException {
+            synchronized (mLock) {
+                return MmTelFeature.this.getEcbm();
+            }
+        }
+
+        @Override
+        public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
+            synchronized (mLock) {
+                MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage);
+            }
+        }
+
+        @Override
+        public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+            synchronized (mLock) {
+                return MmTelFeature.this.getMultiEndpoint();
+            }
+        }
+
+        @Override
+        public int queryCapabilityStatus() throws RemoteException {
+            return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
+        }
+
+        @Override
+        public void addCapabilityCallback(IImsCapabilityCallback c) {
+            MmTelFeature.this.addCapabilityCallback(c);
+        }
+
+        @Override
+        public void removeCapabilityCallback(IImsCapabilityCallback c) {
+            MmTelFeature.this.removeCapabilityCallback(c);
+        }
+
+        @Override
+        public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
+                IImsCapabilityCallback c) throws RemoteException {
+            MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
+        }
+
+        @Override
+        public void queryCapabilityConfiguration(int capability, int radioTech,
+                IImsCapabilityCallback c) {
+            queryCapabilityConfigurationInternal(capability, radioTech, c);
+        }
+    };
+
+    /**
+     * Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask.
+     * The capabilities that are used in MmTelFeature are defined by {@link MmTelCapability}.
+     *
+     * The capabilities of this MmTelFeature will be set by the framework and can be queried with
+     * {@link #queryCapabilityStatus()}.
+     *
+     * This MmTelFeature can then return the status of each of these capabilities (enabled or not)
+     * by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current
+     * status can also be queried using {@link #queryCapabilityStatus()}.
+     */
+    public static class MmTelCapabilities extends Capabilities {
+
+        @VisibleForTesting
+        public MmTelCapabilities() {
+            super();
+        }
+
+        public MmTelCapabilities(Capabilities c) {
+            mCapabilities = c.mCapabilities;
+        }
+
+        @IntDef(flag = true,
+                value = {
+                        CAPABILITY_TYPE_VOICE,
+                        CAPABILITY_TYPE_VIDEO,
+                        CAPABILITY_TYPE_UT
+                })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface MmTelCapability {}
+
+        /**
+         * This MmTelFeature supports Voice calling (IR.92)
+         */
+        public static final int CAPABILITY_TYPE_VOICE = 1 << 0;
+
+        /**
+         * This MmTelFeature supports Video (IR.94)
+         */
+        public static final int CAPABILITY_TYPE_VIDEO = 1 << 1;
+
+        /**
+         * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92)
+         */
+        public static final int CAPABILITY_TYPE_UT = 1 << 2;
+
+        @Override
+        public final void addCapabilities(@MmTelCapability int capabilities) {
+            super.addCapabilities(capabilities);
+        }
+
+        @Override
+        public final void removeCapabilities(@MmTelCapability int capability) {
+            super.removeCapabilities(capability);
+        }
+
+        @Override
+        public final boolean isCapable(@MmTelCapability int capabilities) {
+            return super.isCapable(capabilities);
+        }
+    }
+
+    /**
+     * Listener that the framework implements for communication from the MmTelFeature.
+     */
+    public static class Listener extends IImsMmTelListener.Stub {
+
+        @Override
+        public final void onIncomingCall(IImsCallSession c) {
+            onIncomingCall(new ImsCallSession(c));
+        }
+
+        /**
+         * Called when the IMS provider receives an incoming call.
+         * @param c The {@link ImsCallSession} associated with the new call.
+         */
+        public void onIncomingCall(ImsCallSession c) {
+        }
+    }
+
+    // Lock for feature synchronization
+    private final Object mLock = new Object();
+    private IImsMmTelListener mListener;
+
+    /**
+     * @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and
+     *     notifies the framework.
+     */
+    private void setListener(IImsMmTelListener listener) {
+        synchronized (mLock) {
+            mListener = listener;
+        }
+    }
+
+    private void queryCapabilityConfigurationInternal(int capability, int radioTech,
+            IImsCapabilityCallback c) {
+        boolean enabled = queryCapabilityConfiguration(capability, radioTech);
+        try {
+            if (c != null) {
+                c.onQueryCapabilityConfiguration(capability, radioTech, enabled);
+            }
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!");
+        }
+    }
+
+    /**
+     * The current capability status that this MmTelFeature has defined is available. This
+     * configuration will be used by the platform to figure out which capabilities are CURRENTLY
+     * available to be used.
+     *
+     * Should be a subset of the capabilities that are enabled by the framework in
+     * {@link #changeEnabledCapabilities}.
+     * @return A copy of the current MmTelFeature capability status.
+     */
+    @Override
+    public final MmTelCapabilities queryCapabilityStatus() {
+        return new MmTelCapabilities(super.queryCapabilityStatus());
+    }
+
+    /**
+     * Notify the framework that the status of the Capabilities has changed. Even though the
+     * MmTelFeature capability may be enabled by the framework, the status may be disabled due to
+     * the feature being unavailable from the network.
+     * @param c The current capability status of the MmTelFeature. If a capability is disabled, then
+     * the status of that capability is disabled. This can happen if the network does not currently
+     * support the capability that is enabled. A capability that is disabled by the framework (via
+     * {@link #changeEnabledCapabilities}) should also show the status as disabled.
+     */
+    protected final void notifyCapabilitiesStatusChanged(MmTelCapabilities c) {
+        super.notifyCapabilitiesStatusChanged(c);
+    }
+
+    /**
+     * Notify the framework of an incoming call.
+     * @param c The {@link ImsCallSession} of the new incoming call.
+     *
+     * @throws RemoteException if the connection to the framework is not available. If this happens,
+     *     the call should be no longer considered active and should be cleaned up.
+     * */
+    protected final void notifyIncomingCall(ImsCallSession c) throws RemoteException {
+        synchronized (mLock) {
+            if (mListener == null) {
+                throw new IllegalStateException("Session is not available.");
+            }
+            mListener.onIncomingCall(c.getSession());
+        }
+    }
+
+    /**
+     * Provides the MmTelFeature with the ability to return the framework Capability Configuration
+     * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and
+     * includes a capability A to enable or disable, this method should return the correct enabled
+     * status for capability A.
+     * @param capability The capability that we are querying the configuration for.
+     * @return true if the capability is enabled, false otherwise.
+     */
+    public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability,
+            @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) {
+        // Base implementation - Override to provide functionality
+        return false;
+    }
+
+    /**
+     * The MmTelFeature should override this method to handle the enabling/disabling of
+     * MmTel Features, defined in {@link MmTelCapabilities.MmTelCapability}. The framework assumes
+     * the {@link CapabilityChangeRequest} was processed successfully. If a subset of capabilities
+     * could not be set to their new values,
+     * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} must be called
+     * individually for each capability whose processing resulted in an error.
+     *
+     * Enabling/Disabling a capability here indicates that the capability should be registered or
+     * deregistered (depending on the capability change) and become available or unavailable to
+     * the framework.
+     */
+    @Override
+    public void changeEnabledCapabilities(CapabilityChangeRequest request,
+            CapabilityCallbackProxy c) {
+        // Base implementation, no-op
+    }
+
+    /**
+     * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
+     *
+     * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+     *        {@link ImsCallProfile#SERVICE_TYPE_NONE}
+     *        {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+     *        {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+     * @param callType a call type that is specified in {@link ImsCallProfile}
+     *        {@link ImsCallProfile#CALL_TYPE_VOICE}
+     *        {@link ImsCallProfile#CALL_TYPE_VT}
+     *        {@link ImsCallProfile#CALL_TYPE_VT_TX}
+     *        {@link ImsCallProfile#CALL_TYPE_VT_RX}
+     *        {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
+     *        {@link ImsCallProfile#CALL_TYPE_VS}
+     *        {@link ImsCallProfile#CALL_TYPE_VS_TX}
+     *        {@link ImsCallProfile#CALL_TYPE_VS_RX}
+     * @return a {@link ImsCallProfile} object
+     */
+    public ImsCallProfile createCallProfile(int callSessionType, int callType) {
+        // Base Implementation - Should be overridden
+        return null;
+    }
+
+    /**
+     * Creates an {@link ImsCallSession} with the specified call profile.
+     * Use other methods, if applicable, instead of interacting with
+     * {@link ImsCallSession} directly.
+     *
+     * @param profile a call profile to make the call
+     * @param listener An implementation of IImsCallSessionListener.
+     */
+    public ImsCallSession createCallSession(ImsCallProfile profile,
+            ImsCallSessionListener listener) {
+        // Base Implementation - Should be overridden
+        return null;
+    }
+
+    /**
+     * @return The Ut interface for the supplementary service configuration.
+     */
+    public ImsUtImplBase getUt() {
+        // Base Implementation - Should be overridden
+        return null;
+    }
+
+    /**
+     * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
+     */
+    public ImsEcbmImplBase getEcbm() {
+        // Base Implementation - Should be overridden
+        return null;
+    }
+
+    /**
+     * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
+     */
+    public ImsMultiEndpointImplBase getMultiEndpoint() {
+        // Base Implementation - Should be overridden
+        return null;
+    }
+
+    /**
+     * Sets the current UI TTY mode for the MmTelFeature.
+     * @param mode An integer containing the new UI TTY Mode, can consist of
+     *         {@link TelecomManager#TTY_MODE_OFF},
+     *         {@link TelecomManager#TTY_MODE_FULL},
+     *         {@link TelecomManager#TTY_MODE_HCO},
+     *         {@link TelecomManager#TTY_MODE_VCO}
+     * @param onCompleteMessage A {@link Message} to be used when the mode has been set.
+     */
+    void setUiTtyMode(int mode, Message onCompleteMessage) {
+        // Base Implementation - Should be overridden
+    }
+
+    /**{@inheritDoc}*/
+    @Override
+    public void onFeatureRemoved() {
+        // Base Implementation - Should be overridden
+    }
+
+    /**{@inheritDoc}*/
+    @Override
+    public void onFeatureReady() {
+        // Base Implementation - Should be overridden
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public final IImsMmTelFeature getBinder() {
+        return mImsMMTelBinder;
+    }
+}
diff --git a/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java b/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java
new file mode 100644
index 0000000..8d1bd9d
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.internal.feature;
+
+import android.telephony.ims.internal.aidl.IImsRcsFeature;
+
+/**
+ * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
+ * this class and provide implementations of the RcsFeature methods that they support.
+ * @hide
+ */
+
+public class RcsFeature extends ImsFeature {
+
+    private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() {
+        // Empty Default Implementation.
+    };
+
+
+    public RcsFeature() {
+        super();
+    }
+
+    @Override
+    public void changeEnabledCapabilities(CapabilityChangeRequest request,
+            CapabilityCallbackProxy c) {
+        // Do nothing for base implementation.
+    }
+
+    @Override
+    public void onFeatureRemoved() {
+
+    }
+
+    /**{@inheritDoc}*/
+    @Override
+    public void onFeatureReady() {
+
+    }
+
+    @Override
+    public final IImsRcsFeature getBinder() {
+        return mImsRcsBinder;
+    }
+}
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java
new file mode 100644
index 0000000..33aec5d
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.internal.stub;
+
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.telephony.ims.internal.aidl.IImsConfig;
+import android.telephony.ims.internal.aidl.IImsConfigCallback;
+
+import com.android.ims.ImsConfig;
+
+/**
+ * Controls the modification of IMS specific configurations. For more information on the supported
+ * IMS configuration constants, see {@link ImsConfig}.
+ *
+ * @hide
+ */
+
+public class ImsConfigImplBase {
+
+    //TODO: Implement the Binder logic to call base APIs. Need to finish other ImsService Config
+    // work first.
+    private final IImsConfig mBinder = new IImsConfig.Stub() {
+
+        @Override
+        public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException {
+            ImsConfigImplBase.this.addImsConfigCallback(c);
+        }
+
+        @Override
+        public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException {
+            ImsConfigImplBase.this.removeImsConfigCallback(c);
+        }
+
+        @Override
+        public int getConfigInt(int item) throws RemoteException {
+            return Integer.MIN_VALUE;
+        }
+
+        @Override
+        public String getConfigString(int item) throws RemoteException {
+            return null;
+        }
+
+        @Override
+        public int setConfigInt(int item, int value) throws RemoteException {
+            return Integer.MIN_VALUE;
+        }
+
+        @Override
+        public int setConfigString(int item, String value) throws RemoteException {
+            return Integer.MIN_VALUE;
+        }
+    };
+
+    public class Callback extends IImsConfigCallback.Stub {
+
+        @Override
+        public final void onIntConfigChanged(int item, int value) throws RemoteException {
+            onConfigChanged(item, value);
+        }
+
+        @Override
+        public final void onStringConfigChanged(int item, String value) throws RemoteException {
+            onConfigChanged(item, value);
+        }
+
+        /**
+         * Called when the IMS configuration has changed.
+         * @param item the IMS configuration key constant, as defined in ImsConfig.
+         * @param value the new integer value of the IMS configuration constant.
+         */
+        public void onConfigChanged(int item, int value) {
+            // Base Implementation
+        }
+
+        /**
+         * Called when the IMS configuration has changed.
+         * @param item the IMS configuration key constant, as defined in ImsConfig.
+         * @param value the new String value of the IMS configuration constant.
+         */
+        public void onConfigChanged(int item, String value) {
+            // Base Implementation
+        }
+    }
+
+    private final RemoteCallbackList<IImsConfigCallback> mCallbacks = new RemoteCallbackList<>();
+
+    /**
+     * Adds a {@link Callback} to the list of callbacks notified when a value in the configuration
+     * changes.
+     * @param c callback to add.
+     */
+    private void addImsConfigCallback(IImsConfigCallback c) {
+        mCallbacks.register(c);
+    }
+    /**
+     * Removes a {@link Callback} to the list of callbacks notified when a value in the
+     * configuration changes.
+     *
+     * @param c callback to remove.
+     */
+    private void removeImsConfigCallback(IImsConfigCallback c) {
+        mCallbacks.unregister(c);
+    }
+
+    public final IImsConfig getBinder() {
+        return mBinder;
+    }
+
+    /**
+     * Sets the value for IMS service/capabilities parameters by the operator device
+     * management entity. It sets the config item value in the provisioned storage
+     * from which the master value is derived.
+     *
+     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in Integer format.
+     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+     */
+    public int setConfig(int item, int value) {
+        // Base Implementation - To be overridden.
+        return ImsConfig.OperationStatusConstants.FAILED;
+    }
+
+    /**
+     * Sets the value for IMS service/capabilities parameters by the operator device
+     * management entity. It sets the config item value in the provisioned storage
+     * from which the master value is derived.
+     *
+     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @param value in String format.
+     * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+     */
+    public int setConfig(int item, String value) {
+        return ImsConfig.OperationStatusConstants.FAILED;
+    }
+
+    /**
+     * Gets the value for ims service/capabilities parameters from the provisioned
+     * value storage.
+     *
+     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @return value in Integer format.
+     */
+    public int getConfigInt(int item) {
+        return ImsConfig.OperationStatusConstants.FAILED;
+    }
+
+    /**
+     * Gets the value for ims service/capabilities parameters from the provisioned
+     * value storage.
+     *
+     * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+     * @return value in String format.
+     */
+    public String getConfigString(int item) {
+        return null;
+    }
+}
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl
new file mode 100644
index 0000000..e890cf8
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.internal.stub;
+
+parcelable ImsFeatureConfiguration;
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java
new file mode 100644
index 0000000..244c957
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.internal.stub;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.internal.feature.ImsFeature;
+import android.util.ArraySet;
+
+import java.util.Arrays;
+import java.util.Set;
+
+/**
+ * Container class for IMS Feature configuration. This class contains the features that the
+ * ImsService supports, which are defined in {@link ImsFeature.FeatureType}.
+ * @hide
+ */
+public class ImsFeatureConfiguration implements Parcelable {
+    /**
+     * Features that this ImsService supports.
+     */
+    private final Set<Integer> mFeatures;
+
+    /**
+     * Creates an ImsFeatureConfiguration with the features
+     */
+    public static class Builder {
+            ImsFeatureConfiguration mConfig;
+        public Builder() {
+            mConfig = new ImsFeatureConfiguration();
+        }
+
+        /**
+         * @param feature A feature defined in {@link ImsFeature.FeatureType} that this service
+         *         supports.
+         * @return a {@link Builder} to continue constructing the ImsFeatureConfiguration.
+         */
+        public Builder addFeature(@ImsFeature.FeatureType int feature) {
+            mConfig.addFeature(feature);
+            return this;
+        }
+
+        public ImsFeatureConfiguration build() {
+            return mConfig;
+        }
+    }
+
+    /**
+     * Creates with all registration features empty.
+     *
+     * Consider using the provided {@link Builder} to create this configuration instead.
+     */
+    public ImsFeatureConfiguration() {
+        mFeatures = new ArraySet<>();
+    }
+
+    /**
+     * Configuration of the ImsService, which describes which features the ImsService supports
+     * (for registration).
+     * @param features an array of feature integers defined in {@link ImsFeature} that describe
+     * which features this ImsService supports.
+     */
+    public ImsFeatureConfiguration(int[] features) {
+        mFeatures = new ArraySet<>();
+
+        if (features != null) {
+            for (int i : features) {
+                mFeatures.add(i);
+            }
+        }
+    }
+
+    /**
+     * @return an int[] containing the features that this ImsService supports.
+     */
+    public int[] getServiceFeatures() {
+        return mFeatures.stream().mapToInt(i->i).toArray();
+    }
+
+    void addFeature(int feature) {
+        mFeatures.add(feature);
+    }
+
+    protected ImsFeatureConfiguration(Parcel in) {
+        int[] features = in.createIntArray();
+        if (features != null) {
+            mFeatures = new ArraySet<>(features.length);
+            for(Integer i : features) {
+                mFeatures.add(i);
+            }
+        } else {
+            mFeatures = new ArraySet<>();
+        }
+    }
+
+    public static final Creator<ImsFeatureConfiguration> CREATOR
+            = new Creator<ImsFeatureConfiguration>() {
+        @Override
+        public ImsFeatureConfiguration createFromParcel(Parcel in) {
+            return new ImsFeatureConfiguration(in);
+        }
+
+        @Override
+        public ImsFeatureConfiguration[] newArray(int size) {
+            return new ImsFeatureConfiguration[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeIntArray(mFeatures.stream().mapToInt(i->i).toArray());
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof ImsFeatureConfiguration)) return false;
+
+        ImsFeatureConfiguration that = (ImsFeatureConfiguration) o;
+
+        return mFeatures.equals(that.mFeatures);
+    }
+
+    @Override
+    public int hashCode() {
+        return mFeatures.hashCode();
+    }
+}
diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java
new file mode 100644
index 0000000..558b009
--- /dev/null
+++ b/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.ims.internal.stub;
+
+import android.annotation.IntDef;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.telephony.ims.internal.aidl.IImsRegistration;
+import android.telephony.ims.internal.aidl.IImsRegistrationCallback;
+import android.util.Log;
+
+import com.android.ims.ImsReasonInfo;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Controls IMS registration for this ImsService and notifies the framework when the IMS
+ * registration for this ImsService has changed status.
+ * @hide
+ */
+
+public class ImsRegistrationImplBase {
+
+    private static final String LOG_TAG = "ImsRegistrationImplBase";
+
+    // Defines the underlying radio technology type that we have registered for IMS over.
+    @IntDef(flag = true,
+            value = {
+                    REGISTRATION_TECH_NONE,
+                    REGISTRATION_TECH_LTE,
+                    REGISTRATION_TECH_IWLAN
+            })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsRegistrationTech {}
+    /**
+     * No registration technology specified, used when we are not registered.
+     */
+    public static final int REGISTRATION_TECH_NONE = -1;
+    /**
+     * IMS is registered to IMS via LTE.
+     */
+    public static final int REGISTRATION_TECH_LTE = 0;
+    /**
+     * IMS is registered to IMS via IWLAN.
+     */
+    public static final int REGISTRATION_TECH_IWLAN = 1;
+
+    // Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current
+    // state.
+    private static final int REGISTRATION_STATE_NOT_REGISTERED = 0;
+    private static final int REGISTRATION_STATE_REGISTERING = 1;
+    private static final int REGISTRATION_STATE_REGISTERED = 2;
+
+
+    /**
+     * Callback class for receiving Registration callback events.
+     */
+    public static class Callback extends IImsRegistrationCallback.Stub {
+
+        /**
+         * Notifies the framework when the IMS Provider is connected to the IMS network.
+         *
+         * @param imsRadioTech the radio access technology. Valid values are defined in
+         * {@link ImsRegistrationTech}.
+         */
+        @Override
+        public void onRegistered(@ImsRegistrationTech int imsRadioTech) {
+        }
+
+        /**
+         * Notifies the framework when the IMS Provider is trying to connect the IMS network.
+         *
+         * @param imsRadioTech the radio access technology. Valid values are defined in
+         * {@link ImsRegistrationTech}.
+         */
+        @Override
+        public void onRegistering(@ImsRegistrationTech int imsRadioTech) {
+        }
+
+        /**
+         * Notifies the framework when the IMS Provider is disconnected from the IMS network.
+         *
+         * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+         */
+        @Override
+        public void onDeregistered(ImsReasonInfo info) {
+        }
+
+        /**
+         * A failure has occurred when trying to handover registration to another technology type,
+         * defined in {@link ImsRegistrationTech}
+         *
+         * @param imsRadioTech The {@link ImsRegistrationTech} type that has failed
+         * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
+         */
+        @Override
+        public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
+                ImsReasonInfo info) {
+        }
+    }
+
+    private final IImsRegistration mBinder = new IImsRegistration.Stub() {
+
+        @Override
+        public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException {
+            return getConnectionType();
+        }
+
+        @Override
+        public void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
+            ImsRegistrationImplBase.this.addRegistrationCallback(c);
+        }
+
+        @Override
+        public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
+            ImsRegistrationImplBase.this.removeRegistrationCallback(c);
+        }
+    };
+
+    private final RemoteCallbackList<IImsRegistrationCallback> mCallbacks
+            = new RemoteCallbackList<>();
+    private final Object mLock = new Object();
+    // Locked on mLock
+    private @ImsRegistrationTech
+    int mConnectionType = REGISTRATION_TECH_NONE;
+    // Locked on mLock
+    private int mRegistrationState = REGISTRATION_STATE_NOT_REGISTERED;
+    // Locked on mLock
+    private ImsReasonInfo mLastDisconnectCause;
+
+    public final IImsRegistration getBinder() {
+        return mBinder;
+    }
+
+    private void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
+        mCallbacks.register(c);
+        updateNewCallbackWithState(c);
+    }
+
+    private void removeRegistrationCallback(IImsRegistrationCallback c) {
+        mCallbacks.unregister(c);
+    }
+
+    /**
+     * Notify the framework that the device is connected to the IMS network.
+     *
+     * @param imsRadioTech the radio access technology. Valid values are defined in
+     * {@link ImsRegistrationTech}.
+     */
+    public final void onRegistered(@ImsRegistrationTech int imsRadioTech) {
+        updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERED);
+        mCallbacks.broadcast((c) -> {
+            try {
+                c.onRegistered(imsRadioTech);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, e + " " + "onRegistrationConnected() - Skipping " +
+                        "callback.");
+            }
+        });
+    }
+
+    /**
+     * Notify the framework that the device is trying to connect the IMS network.
+     *
+     * @param imsRadioTech the radio access technology. Valid values are defined in
+     * {@link ImsRegistrationTech}.
+     */
+    public final void onRegistering(@ImsRegistrationTech int imsRadioTech) {
+        updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERING);
+        mCallbacks.broadcast((c) -> {
+            try {
+                c.onRegistering(imsRadioTech);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, e + " " + "onRegistrationProcessing() - Skipping " +
+                        "callback.");
+            }
+        });
+    }
+
+    /**
+     * Notify the framework that the device is disconnected from the IMS network.
+     *
+     * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
+     */
+    public final void onDeregistered(ImsReasonInfo info) {
+        updateToDisconnectedState(info);
+        mCallbacks.broadcast((c) -> {
+            try {
+                c.onDeregistered(info);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, e + " " + "onRegistrationDisconnected() - Skipping " +
+                        "callback.");
+            }
+        });
+    }
+
+    public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
+            ImsReasonInfo info) {
+        mCallbacks.broadcast((c) -> {
+            try {
+                c.onTechnologyChangeFailed(imsRadioTech, info);
+            } catch (RemoteException e) {
+                Log.w(LOG_TAG, e + " " + "onRegistrationChangeFailed() - Skipping " +
+                        "callback.");
+            }
+        });
+    }
+
+    private void updateToState(@ImsRegistrationTech int connType, int newState) {
+        synchronized (mLock) {
+            mConnectionType = connType;
+            mRegistrationState = newState;
+            mLastDisconnectCause = null;
+        }
+    }
+
+    private void updateToDisconnectedState(ImsReasonInfo info) {
+        synchronized (mLock) {
+            updateToState(REGISTRATION_TECH_NONE, REGISTRATION_STATE_NOT_REGISTERED);
+            if (info != null) {
+                mLastDisconnectCause = info;
+            } else {
+                Log.w(LOG_TAG, "updateToDisconnectedState: no ImsReasonInfo provided.");
+                mLastDisconnectCause = new ImsReasonInfo();
+            }
+        }
+    }
+
+    private @ImsRegistrationTech int getConnectionType() {
+        synchronized (mLock) {
+            return mConnectionType;
+        }
+    }
+
+    /**
+     * @param c the newly registered callback that will be updated with the current registration
+     *         state.
+     */
+    private void updateNewCallbackWithState(IImsRegistrationCallback c) throws RemoteException {
+        int state;
+        ImsReasonInfo disconnectInfo;
+        synchronized (mLock) {
+            state = mRegistrationState;
+            disconnectInfo = mLastDisconnectCause;
+        }
+        switch (state) {
+            case REGISTRATION_STATE_NOT_REGISTERED: {
+                c.onDeregistered(disconnectInfo);
+                break;
+            }
+            case REGISTRATION_STATE_REGISTERING: {
+                c.onRegistering(getConnectionType());
+                break;
+            }
+            case REGISTRATION_STATE_REGISTERED: {
+                c.onRegistered(getConnectionType());
+                break;
+            }
+        }
+    }
+}
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
index d267ad2..0dbc186 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -441,10 +441,11 @@
 
     @Override
     public String toString() {
-        return "SmsCbHeader{GS=" + mGeographicalScope + ", serialNumber=0x" +
-                Integer.toHexString(mSerialNumber) +
-                ", messageIdentifier=0x" + Integer.toHexString(mMessageIdentifier) +
-                ", DCS=0x" + Integer.toHexString(mDataCodingScheme) +
-                ", page " + mPageIndex + " of " + mNrOfPages + '}';
+        return "SmsCbHeader{GS=" + mGeographicalScope + ", serialNumber=0x"
+                + Integer.toHexString(mSerialNumber)
+                + ", messageIdentifier=0x" + Integer.toHexString(mMessageIdentifier)
+                + ", format=" + mFormat
+                + ", DCS=0x" + Integer.toHexString(mDataCodingScheme)
+                + ", page " + mPageIndex + " of " + mNrOfPages + '}';
     }
 }
\ No newline at end of file
diff --git a/test-base/Android.bp b/test-base/Android.bp
index a3fd345..30c9af1 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -25,6 +25,7 @@
     srcs: ["src/**/*.java"],
 
     no_framework_libs: true,
+    hostdex: true,
     libs: [
         "framework",
     ],
diff --git a/tests/SurfaceComposition/Android.mk b/tests/SurfaceComposition/Android.mk
index 1962791..f59458d 100644
--- a/tests/SurfaceComposition/Android.mk
+++ b/tests/SurfaceComposition/Android.mk
@@ -29,7 +29,7 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := junit
 
-LOCAL_JAVA_LIBRARIES := android.test.runner.stubs
+LOCAL_JAVA_LIBRARIES := android.test.runner.stubs android.test.base.stubs
 
 LOCAL_PACKAGE_NAME := SurfaceComposition
 
diff --git a/tests/WindowAnimationJank/Android.mk b/tests/WindowAnimationJank/Android.mk
index 8aac8a1..7800a80 100644
--- a/tests/WindowAnimationJank/Android.mk
+++ b/tests/WindowAnimationJank/Android.mk
@@ -29,6 +29,8 @@
     ub-janktesthelper \
     junit
 
+LOCAL_JAVA_LIBRARIES := android.test.base.stubs
+
 LOCAL_SDK_VERSION := current
 
 include $(BUILD_PACKAGE)
diff --git a/tests/net/java/android/net/IpSecManagerTest.java b/tests/net/java/android/net/IpSecManagerTest.java
index ccb0f3b..0f40b45 100644
--- a/tests/net/java/android/net/IpSecManagerTest.java
+++ b/tests/net/java/android/net/IpSecManagerTest.java
@@ -80,7 +80,7 @@
         int resourceId = 1;
         IpSecSpiResponse spiResp =
                 new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
-        when(mMockIpSecService.reserveSecurityParameterIndex(
+        when(mMockIpSecService.allocateSecurityParameterIndex(
                         eq(IpSecTransform.DIRECTION_IN),
                         eq(GOOGLE_DNS_4.getHostAddress()),
                         eq(DROID_SPI),
@@ -88,7 +88,7 @@
                 .thenReturn(spiResp);
 
         IpSecManager.SecurityParameterIndex droidSpi =
-                mIpSecManager.reserveSecurityParameterIndex(
+                mIpSecManager.allocateSecurityParameterIndex(
                         IpSecTransform.DIRECTION_IN, GOOGLE_DNS_4, DROID_SPI);
         assertEquals(DROID_SPI, droidSpi.getSpi());
 
@@ -102,7 +102,7 @@
         int resourceId = 1;
         IpSecSpiResponse spiResp =
                 new IpSecSpiResponse(IpSecManager.Status.OK, resourceId, DROID_SPI);
-        when(mMockIpSecService.reserveSecurityParameterIndex(
+        when(mMockIpSecService.allocateSecurityParameterIndex(
                         eq(IpSecTransform.DIRECTION_OUT),
                         eq(GOOGLE_DNS_4.getHostAddress()),
                         eq(IpSecManager.INVALID_SECURITY_PARAMETER_INDEX),
@@ -110,7 +110,7 @@
                 .thenReturn(spiResp);
 
         IpSecManager.SecurityParameterIndex randomSpi =
-                mIpSecManager.reserveSecurityParameterIndex(
+                mIpSecManager.allocateSecurityParameterIndex(
                         IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
 
         assertEquals(DROID_SPI, randomSpi.getSpi());
@@ -127,12 +127,13 @@
     public void testAllocSpiResUnavaiableExeption() throws Exception {
         IpSecSpiResponse spiResp =
                 new IpSecSpiResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE, 0, 0);
-        when(mMockIpSecService.reserveSecurityParameterIndex(
+        when(mMockIpSecService.allocateSecurityParameterIndex(
                         anyInt(), anyString(), anyInt(), anyObject()))
                 .thenReturn(spiResp);
 
         try {
-            mIpSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+            mIpSecManager.allocateSecurityParameterIndex(
+                    IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
             fail("ResourceUnavailableException was not thrown");
         } catch (IpSecManager.ResourceUnavailableException e) {
         }
@@ -144,12 +145,13 @@
     @Test
     public void testAllocSpiSpiUnavaiableExeption() throws Exception {
         IpSecSpiResponse spiResp = new IpSecSpiResponse(IpSecManager.Status.SPI_UNAVAILABLE, 0, 0);
-        when(mMockIpSecService.reserveSecurityParameterIndex(
+        when(mMockIpSecService.allocateSecurityParameterIndex(
                         anyInt(), anyString(), anyInt(), anyObject()))
                 .thenReturn(spiResp);
 
         try {
-            mIpSecManager.reserveSecurityParameterIndex(IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
+            mIpSecManager.allocateSecurityParameterIndex(
+                    IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4);
             fail("ResourceUnavailableException was not thrown");
         } catch (IpSecManager.ResourceUnavailableException e) {
         }
@@ -161,7 +163,7 @@
     @Test
     public void testRequestAllocInvalidSpi() throws Exception {
         try {
-            mIpSecManager.reserveSecurityParameterIndex(
+            mIpSecManager.allocateSecurityParameterIndex(
                     IpSecTransform.DIRECTION_OUT, GOOGLE_DNS_4, 0);
             fail("Able to allocate invalid spi");
         } catch (IllegalArgumentException e) {
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 20a4897..80e42a3 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -125,7 +125,7 @@
                 .thenReturn(TEST_SPI_OUT);
 
         IpSecSpiResponse spiResp =
-                mIpSecService.reserveSecurityParameterIndex(
+                mIpSecService.allocateSecurityParameterIndex(
                         IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
         assertEquals(IpSecManager.Status.OK, spiResp.status);
         assertEquals(TEST_SPI_OUT, spiResp.spi);
@@ -142,7 +142,7 @@
                 .thenReturn(TEST_SPI_OUT);
 
         IpSecSpiResponse spiResp =
-                mIpSecService.reserveSecurityParameterIndex(
+                mIpSecService.allocateSecurityParameterIndex(
                         IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
 
         mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId);
@@ -178,7 +178,7 @@
                 .thenReturn(TEST_SPI_OUT);
 
         IpSecSpiResponse spiResp =
-                mIpSecService.reserveSecurityParameterIndex(
+                mIpSecService.allocateSecurityParameterIndex(
                         IpSecTransform.DIRECTION_OUT, mRemoteAddr, TEST_SPI_OUT, new Binder());
 
         IpSecService.UserRecord userRecord =
@@ -212,7 +212,7 @@
                 .thenReturn(returnSpi);
 
         IpSecSpiResponse spi =
-                mIpSecService.reserveSecurityParameterIndex(
+                mIpSecService.allocateSecurityParameterIndex(
                         direction,
                         NetworkUtils.numericToInetAddress(remoteAddress).getHostAddress(),
                         IpSecManager.INVALID_SECURITY_PARAMETER_INDEX,
diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java
index 354ad2a..8683c12 100644
--- a/tests/net/java/com/android/server/IpSecServiceTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceTest.java
@@ -287,7 +287,7 @@
         for (String address : invalidAddresses) {
             try {
                 IpSecSpiResponse spiResp =
-                        mIpSecService.reserveSecurityParameterIndex(
+                        mIpSecService.allocateSecurityParameterIndex(
                                 IpSecTransform.DIRECTION_OUT, address, DROID_SPI, new Binder());
                 fail("Invalid address was passed through IpSecService validation: " + address);
             } catch (IllegalArgumentException e) {
@@ -368,7 +368,7 @@
         // Reserve spis until it fails.
         for (int i = 0; i < MAX_NUM_SPIS; i++) {
             IpSecSpiResponse newSpi =
-                    mIpSecService.reserveSecurityParameterIndex(
+                    mIpSecService.allocateSecurityParameterIndex(
                             0x1,
                             InetAddress.getLoopbackAddress().getHostAddress(),
                             DROID_SPI + i,
@@ -384,7 +384,7 @@
 
         // Try to reserve one more SPI, and should fail.
         IpSecSpiResponse extraSpi =
-                mIpSecService.reserveSecurityParameterIndex(
+                mIpSecService.allocateSecurityParameterIndex(
                         0x1,
                         InetAddress.getLoopbackAddress().getHostAddress(),
                         DROID_SPI + MAX_NUM_SPIS,
@@ -398,7 +398,7 @@
 
         // Should successfully reserve one more spi.
         extraSpi =
-                mIpSecService.reserveSecurityParameterIndex(
+                mIpSecService.allocateSecurityParameterIndex(
                         0x1,
                         InetAddress.getLoopbackAddress().getHostAddress(),
                         DROID_SPI + MAX_NUM_SPIS,
diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
index 94686c0..2da294c 100644
--- a/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/Android.mk
@@ -20,6 +20,7 @@
 LOCAL_USE_AAPT2 := true
 LOCAL_AAPT_NAMESPACES := true
 LOCAL_PACKAGE_NAME := AaptTestNamespace_App
+LOCAL_EXPORT_PACKAGE_RESOURCES := true
 LOCAL_MODULE_TAGS := tests
 LOCAL_SRC_FILES := $(call all-java-files-under,src)
 LOCAL_STATIC_ANDROID_LIBRARIES := \
diff --git a/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml b/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml
index 1b80d95..174e746 100644
--- a/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml
+++ b/tools/aapt2/integration-tests/NamespaceTest/App/res/values/values.xml
@@ -26,4 +26,4 @@
             @com.android.aapt.namespace.libtwo:string/public_string
         </item>
     </style>
-</resources>
\ No newline at end of file
+</resources>
diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk b/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk
new file mode 100644
index 0000000..a35f6ed
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/Split/Android.mk
@@ -0,0 +1,28 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_USE_AAPT2 := true
+LOCAL_AAPT_NAMESPACES := true
+LOCAL_PACKAGE_NAME := AaptTestNamespace_Split
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_APK_LIBRARIES := AaptTestNamespace_App
+LOCAL_RES_LIBRARIES := AaptTestNamespace_App
+LOCAL_AAPT_FLAGS := --package-id 0x80 --rename-manifest-package com.android.aapt.namespace.app
+include $(BUILD_PACKAGE)
diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/AndroidManifest.xml b/tools/aapt2/integration-tests/NamespaceTest/Split/AndroidManifest.xml
new file mode 100644
index 0000000..bc9583b
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/Split/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.aapt.namespace.split"
+    featureSplit="split">
+
+    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="26"/>
+
+    <application>
+        <activity android:name=".SplitActivity" android:theme="@style/ActivityTheme">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/res/values/values.xml b/tools/aapt2/integration-tests/NamespaceTest/Split/res/values/values.xml
new file mode 100644
index 0000000..63da552
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/Split/res/values/values.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <string name="activity_name">Namespace Split</string>
+
+    <style name="ActivityTheme" parent="com.android.aapt.namespace.libone:style/Theme">
+        <item name="android:colorPrimary">#FF9800</item>
+        <item name="android:colorPrimaryDark">#E65100</item>
+        <item name="android:colorAccent">#FFD180</item>
+    </style>
+</resources>
diff --git a/tools/aapt2/integration-tests/NamespaceTest/Split/src/com/android/aapt/namespace/split/SplitActivity.java b/tools/aapt2/integration-tests/NamespaceTest/Split/src/com/android/aapt/namespace/split/SplitActivity.java
new file mode 100644
index 0000000..3fff3cf
--- /dev/null
+++ b/tools/aapt2/integration-tests/NamespaceTest/Split/src/com/android/aapt/namespace/split/SplitActivity.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.aapt.namespace.split;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class SplitActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 2d517c7..0cfc0bd 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -30,7 +30,8 @@
 #include "ValueVisitor.h"
 #include "util/Util.h"
 
-using android::StringPiece;
+using ::android::StringPiece;
+using ::android::StringPiece16;
 
 namespace aapt {
 
@@ -291,11 +292,34 @@
   const std::u16string package16 = util::Utf8ToUtf16(name.package);
   const std::u16string type16 = util::Utf8ToUtf16(to_string(name.type));
   const std::u16string entry16 = util::Utf8ToUtf16(name.entry);
+  const std::u16string mangled_entry16 =
+      util::Utf8ToUtf16(NameMangler::MangleEntry(name.package, name.entry));
 
-  uint32_t type_spec_flags = 0;
-  ResourceId res_id = table.identifierForName(
-      entry16.data(), entry16.size(), type16.data(), type16.size(),
-      package16.data(), package16.size(), &type_spec_flags);
+  uint32_t type_spec_flags;
+  ResourceId res_id;
+
+  // There can be mangled resources embedded within other packages. Here we will
+  // look into each package and look-up the mangled name until we find the resource.
+  const size_t count = table.getBasePackageCount();
+  for (size_t i = 0; i < count; i++) {
+    const android::String16 package_name = table.getBasePackageName(i);
+    StringPiece16 real_package16 = package16;
+    StringPiece16 real_entry16 = entry16;
+    std::u16string scratch_entry16;
+    if (StringPiece16(package_name) != package16) {
+      real_entry16 = mangled_entry16;
+      real_package16 = package_name.string();
+    }
+
+    type_spec_flags = 0;
+    res_id = table.identifierForName(real_entry16.data(), real_entry16.size(), type16.data(),
+                                     type16.size(), real_package16.data(), real_package16.size(),
+                                     &type_spec_flags);
+    if (res_id.is_valid()) {
+      break;
+    }
+  }
+
   if (!res_id.is_valid()) {
     return {};
   }
diff --git a/tools/aapt2/process/SymbolTable_test.cpp b/tools/aapt2/process/SymbolTable_test.cpp
index fd8a508..1f59d70 100644
--- a/tools/aapt2/process/SymbolTable_test.cpp
+++ b/tools/aapt2/process/SymbolTable_test.cpp
@@ -16,7 +16,15 @@
 
 #include "process/SymbolTable.h"
 
+#include "SdkConstants.h"
+#include "format/binary/TableFlattener.h"
 #include "test/Test.h"
+#include "util/BigBuffer.h"
+
+using ::testing::Eq;
+using ::testing::IsNull;
+using ::testing::Ne;
+using ::testing::NotNull;
 
 namespace aapt {
 
@@ -30,13 +38,13 @@
           .Build();
 
   ResourceTableSymbolSource symbol_source(table.get());
-  EXPECT_NE(nullptr, symbol_source.FindByName(test::ParseNameOrDie("android:id/foo")));
-  EXPECT_NE(nullptr, symbol_source.FindByName(test::ParseNameOrDie("android:id/bar")));
+  EXPECT_THAT(symbol_source.FindByName(test::ParseNameOrDie("android:id/foo")), NotNull());
+  EXPECT_THAT(symbol_source.FindByName(test::ParseNameOrDie("android:id/bar")), NotNull());
 
   std::unique_ptr<SymbolTable::Symbol> s =
       symbol_source.FindByName(test::ParseNameOrDie("android:attr/foo"));
-  ASSERT_NE(nullptr, s);
-  EXPECT_NE(nullptr, s->attribute);
+  ASSERT_THAT(s, NotNull());
+  EXPECT_THAT(s->attribute, NotNull());
 }
 
 TEST(ResourceTableSymbolSourceTest, FindPrivateAttrSymbol) {
@@ -49,8 +57,8 @@
   ResourceTableSymbolSource symbol_source(table.get());
   std::unique_ptr<SymbolTable::Symbol> s =
       symbol_source.FindByName(test::ParseNameOrDie("android:attr/foo"));
-  ASSERT_NE(nullptr, s);
-  EXPECT_NE(nullptr, s->attribute);
+  ASSERT_THAT(s, NotNull());
+  EXPECT_THAT(s->attribute, NotNull());
 }
 
 TEST(SymbolTableTest, FindByName) {
@@ -64,8 +72,52 @@
   SymbolTable symbol_table(&mangler);
   symbol_table.AppendSource(util::make_unique<ResourceTableSymbolSource>(table.get()));
 
-  EXPECT_NE(nullptr, symbol_table.FindByName(test::ParseNameOrDie("id/foo")));
-  EXPECT_NE(nullptr, symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")));
+  EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("id/foo")), NotNull());
+  EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")), NotNull());
+}
+
+TEST(SymbolTableTest, FindByNameWhenSymbolIsMangledInResTable) {
+  using namespace android;
+
+  std::unique_ptr<IAaptContext> context =
+      test::ContextBuilder()
+          .SetCompilationPackage("com.android.app")
+          .SetPackageId(0x7f)
+          .SetPackageType(PackageType::kApp)
+          .SetMinSdkVersion(SDK_LOLLIPOP_MR1)
+          .SetNameManglerPolicy(NameManglerPolicy{"com.android.app"})
+          .Build();
+
+  // Create a ResourceTable with a mangled resource, simulating a static library being merged into
+  // the main application package.
+  std::unique_ptr<ResourceTable> table =
+      test::ResourceTableBuilder()
+          .AddSimple("com.android.app:id/" + NameMangler::MangleEntry("com.android.lib", "foo"),
+                     ResourceId(0x7f020000))
+          .AddSimple("com.android.app:id/bar", ResourceId(0x7f020001))
+          .Build();
+
+  BigBuffer buffer(1024u);
+  TableFlattener flattener({}, &buffer);
+  ASSERT_TRUE(flattener.Consume(context.get(), table.get()));
+
+  std::unique_ptr<uint8_t[]> data = util::Copy(buffer);
+
+  // Construct the test AssetManager.
+  auto asset_manager_source = util::make_unique<AssetManagerSymbolSource>();
+  ResTable& res_table = const_cast<ResTable&>(
+      asset_manager_source->GetAssetManager()->getResources(false /*required*/));
+  ASSERT_THAT(res_table.add(data.get(), buffer.size()), Eq(NO_ERROR));
+
+  SymbolTable symbol_table(context->GetNameMangler());
+  symbol_table.AppendSource(std::move(asset_manager_source));
+
+  EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/foo")), NotNull());
+  EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.app:id/bar")), NotNull());
+
+  EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.app:id/foo")), IsNull());
+  EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.lib:id/bar")), IsNull());
+  EXPECT_THAT(symbol_table.FindByName(test::ParseNameOrDie("com.android.other:id/foo")), IsNull());
 }
 
 }  // namespace aapt
diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp
index 468864b..e301eb1 100644
--- a/tools/stats_log_api_gen/Android.bp
+++ b/tools/stats_log_api_gen/Android.bp
@@ -97,6 +97,7 @@
     cflags: [
         "-Wall",
         "-Werror",
+        "-fexceptions",
     ],
     export_generated_headers: ["statslog.h"],
     shared_libs: [
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index f76196d..a25784d 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -111,8 +111,9 @@
             return JAVA_TYPE_UNKNOWN;
         case FieldDescriptor::TYPE_MESSAGE:
             // TODO: not the final package name
-            if (field->message_type()->full_name() == "android.os.statsd.WorkSource") {
-                return JAVA_TYPE_WORK_SOURCE;
+            if (field->message_type()->full_name() ==
+                "android.os.statsd.AttributionChain") {
+              return JAVA_TYPE_ATTRIBUTION_CHAIN;
             } else {
                 return JAVA_TYPE_OBJECT;
             }
@@ -136,138 +137,153 @@
 }
 
 /**
+ * Gather the info about an atom proto.
+ */
+int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
+                 vector<java_type_t> *signature) {
+
+  int errorCount = 0;
+  // Build a sorted list of the fields. Descriptor has them in source file
+  // order.
+  map<int, const FieldDescriptor *> fields;
+  for (int j = 0; j < atom->field_count(); j++) {
+    const FieldDescriptor *field = atom->field(j);
+    fields[field->number()] = field;
+  }
+
+  // Check that the parameters start at 1 and go up sequentially.
+  int expectedNumber = 1;
+  for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
+       it != fields.end(); it++) {
+    const int number = it->first;
+    const FieldDescriptor *field = it->second;
+    if (number != expectedNumber) {
+      print_error(field,
+                  "Fields must be numbered consecutively starting at 1:"
+                  " '%s' is %d but should be %d\n",
+                  field->name().c_str(), number, expectedNumber);
+      errorCount++;
+      expectedNumber = number;
+      continue;
+    }
+    expectedNumber++;
+  }
+
+  // Check that only allowed types are present. Remove any invalid ones.
+  for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
+       it != fields.end(); it++) {
+    const FieldDescriptor *field = it->second;
+
+    java_type_t javaType = java_type(field);
+
+    if (javaType == JAVA_TYPE_UNKNOWN) {
+      print_error(field, "Unkown type for field: %s\n", field->name().c_str());
+      errorCount++;
+      continue;
+    } else if (javaType == JAVA_TYPE_OBJECT) {
+      // Allow attribution chain, but only at position 1.
+      print_error(field, "Message type not allowed for field: %s\n",
+                  field->name().c_str());
+      errorCount++;
+      continue;
+    } else if (javaType == JAVA_TYPE_BYTE_ARRAY) {
+      print_error(field, "Raw bytes type not allowed for field: %s\n",
+                  field->name().c_str());
+      errorCount++;
+      continue;
+    }
+  }
+
+  // Check that if there's an attribution chain, it's at position 1.
+  for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
+       it != fields.end(); it++) {
+    int number = it->first;
+    if (number != 1) {
+      const FieldDescriptor *field = it->second;
+      java_type_t javaType = java_type(field);
+      if (javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+        print_error(
+            field,
+            "AttributionChain fields must have field id 1, in message: '%s'\n",
+            atom->name().c_str());
+        errorCount++;
+      }
+    }
+  }
+
+  // Build the type signature and the atom data.
+  for (map<int, const FieldDescriptor *>::const_iterator it = fields.begin();
+       it != fields.end(); it++) {
+    const FieldDescriptor *field = it->second;
+    java_type_t javaType = java_type(field);
+
+    AtomField atField(field->name(), javaType);
+    if (javaType == JAVA_TYPE_ENUM) {
+      // All enums are treated as ints when it comes to function signatures.
+      signature->push_back(JAVA_TYPE_INT);
+      const EnumDescriptor *enumDescriptor = field->enum_type();
+      for (int i = 0; i < enumDescriptor->value_count(); i++) {
+        atField.enumValues[enumDescriptor->value(i)->number()] =
+            enumDescriptor->value(i)->name().c_str();
+      }
+    } else {
+      signature->push_back(javaType);
+    }
+    atomDecl->fields.push_back(atField);
+  }
+
+  return errorCount;
+}
+
+/**
  * Gather the info about the atoms.
  */
-int
-collate_atoms(const Descriptor* descriptor, Atoms* atoms)
-{
-    int errorCount = 0;
-    const bool dbg = false;
+int collate_atoms(const Descriptor *descriptor, Atoms *atoms) {
+  int errorCount = 0;
+  const bool dbg = false;
 
-    for (int i=0; i<descriptor->field_count(); i++) {
-        const FieldDescriptor* atomField = descriptor->field(i);
-
-        if (dbg) {
-            printf("   %s (%d)\n", atomField->name().c_str(), atomField->number());
-        }
-
-        // StatsEvent only has one oneof, which contains only messages. Don't allow other types.
-        if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
-            print_error(atomField,
-                    "Bad type for atom. StatsEvent can only have message type fields: %s\n",
-                    atomField->name().c_str());
-            errorCount++;
-            continue;
-        }
-
-        const Descriptor* atom = atomField->message_type();
-
-        // Build a sorted list of the fields. Descriptor has them in source file order.
-        map<int,const FieldDescriptor*> fields;
-        for (int j=0; j<atom->field_count(); j++) {
-            const FieldDescriptor* field = atom->field(j);
-            fields[field->number()] = field;
-        }
-
-        // Check that the parameters start at 1 and go up sequentially.
-        int expectedNumber = 1;
-        for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
-                it != fields.end(); it++) {
-            const int number = it->first;
-            const FieldDescriptor* field = it->second;
-            if (number != expectedNumber) {
-                print_error(field, "Fields must be numbered consecutively starting at 1:"
-                        " '%s' is %d but should be %d\n",
-                        field->name().c_str(), number, expectedNumber);
-                errorCount++;
-                expectedNumber = number;
-                continue;
-            }
-            expectedNumber++;
-        }
-
-        // Check that only allowed types are present. Remove any invalid ones.
-        for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
-                it != fields.end(); it++) {
-            const FieldDescriptor* field = it->second;
-
-            java_type_t javaType = java_type(field);
-
-            if (javaType == JAVA_TYPE_UNKNOWN) {
-                print_error(field, "Unkown type for field: %s\n", field->name().c_str());
-                errorCount++;
-                continue;
-            } else if (javaType == JAVA_TYPE_OBJECT) {
-                // Allow WorkSources, but only at position 1.
-                print_error(field, "Message type not allowed for field: %s\n",
-                        field->name().c_str());
-                errorCount++;
-                continue;
-            } else if (javaType == JAVA_TYPE_BYTE_ARRAY) {
-                print_error(field, "Raw bytes type not allowed for field: %s\n",
-                        field->name().c_str());
-                errorCount++;
-                continue;
-            }
-        }
-
-        // Check that if there's a WorkSource, it's at position 1.
-        for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
-                it != fields.end(); it++) {
-            int number = it->first;
-            if (number != 1) {
-                const FieldDescriptor* field = it->second;
-                java_type_t javaType = java_type(field);
-                if (javaType == JAVA_TYPE_WORK_SOURCE) {
-                    print_error(field, "WorkSource fields must have field id 1, in message: '%s'\n",
-                           atom->name().c_str());
-                    errorCount++;
-                }
-            }
-        }
-
-        AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
-
-        // Build the type signature and the atom data.
-        vector<java_type_t> signature;
-        for (map<int,const FieldDescriptor*>::const_iterator it = fields.begin();
-                it != fields.end(); it++) {
-            const FieldDescriptor* field = it->second;
-            java_type_t javaType = java_type(field);
-
-            AtomField atField(field->name(), javaType);
-            if (javaType == JAVA_TYPE_ENUM) {
-                // All enums are treated as ints when it comes to function signatures.
-                signature.push_back(JAVA_TYPE_INT);
-                const EnumDescriptor* enumDescriptor = field->enum_type();
-                for (int i = 0; i < enumDescriptor->value_count(); i++) {
-                    atField.enumValues[enumDescriptor->value(i)->number()] =
-                        enumDescriptor->value(i)->name().c_str();
-                }
-            } else {
-                signature.push_back(javaType);
-            }
-            atomDecl.fields.push_back(atField);
-        }
-
-        atoms->signatures.insert(signature);
-        atoms->decls.insert(atomDecl);
-    }
+  for (int i = 0; i < descriptor->field_count(); i++) {
+    const FieldDescriptor *atomField = descriptor->field(i);
 
     if (dbg) {
-        printf("signatures = [\n");
-        for (set<vector<java_type_t>>::const_iterator it = atoms->signatures.begin();
-                it != atoms->signatures.end(); it++) {
-            printf("   ");
-            for (vector<java_type_t>::const_iterator jt = it->begin(); jt != it->end(); jt++) {
-                printf(" %d", (int)*jt);
-            }
-            printf("\n");
-        }
-        printf("]\n");
+      printf("   %s (%d)\n", atomField->name().c_str(), atomField->number());
     }
 
-    return errorCount;
+    // StatsEvent only has one oneof, which contains only messages. Don't allow
+    // other types.
+    if (atomField->type() != FieldDescriptor::TYPE_MESSAGE) {
+      print_error(atomField,
+                  "Bad type for atom. StatsEvent can only have message type "
+                  "fields: %s\n",
+                  atomField->name().c_str());
+      errorCount++;
+      continue;
+    }
+
+    const Descriptor *atom = atomField->message_type();
+    AtomDecl atomDecl(atomField->number(), atomField->name(), atom->name());
+    vector<java_type_t> signature;
+    errorCount += collate_atom(atom, &atomDecl, &signature);
+    atoms->signatures.insert(signature);
+    atoms->decls.insert(atomDecl);
+  }
+
+  if (dbg) {
+    printf("signatures = [\n");
+    for (set<vector<java_type_t>>::const_iterator it =
+             atoms->signatures.begin();
+         it != atoms->signatures.end(); it++) {
+      printf("   ");
+      for (vector<java_type_t>::const_iterator jt = it->begin();
+           jt != it->end(); jt++) {
+        printf(" %d", (int)*jt);
+      }
+      printf("\n");
+    }
+    printf("]\n");
+  }
+
+  return errorCount;
 }
 
 }  // namespace stats_log_api_gen
diff --git a/tools/stats_log_api_gen/Collation.h b/tools/stats_log_api_gen/Collation.h
index 2f840d7..cd0625c 100644
--- a/tools/stats_log_api_gen/Collation.h
+++ b/tools/stats_log_api_gen/Collation.h
@@ -37,22 +37,21 @@
  * The types for atom parameters.
  */
 typedef enum {
-    JAVA_TYPE_UNKNOWN = 0,
+  JAVA_TYPE_UNKNOWN = 0,
 
-    JAVA_TYPE_WORK_SOURCE = 1,
-    JAVA_TYPE_BOOLEAN = 2,
-    JAVA_TYPE_INT = 3,
-    JAVA_TYPE_LONG = 4,
-    JAVA_TYPE_FLOAT = 5,
-    JAVA_TYPE_DOUBLE = 6,
-    JAVA_TYPE_STRING = 7,
-    JAVA_TYPE_ENUM = 8,
+  JAVA_TYPE_ATTRIBUTION_CHAIN = 1,
+  JAVA_TYPE_BOOLEAN = 2,
+  JAVA_TYPE_INT = 3,
+  JAVA_TYPE_LONG = 4,
+  JAVA_TYPE_FLOAT = 5,
+  JAVA_TYPE_DOUBLE = 6,
+  JAVA_TYPE_STRING = 7,
+  JAVA_TYPE_ENUM = 8,
 
-    JAVA_TYPE_OBJECT = -1,
-    JAVA_TYPE_BYTE_ARRAY = -2,
+  JAVA_TYPE_OBJECT = -1,
+  JAVA_TYPE_BYTE_ARRAY = -2,
 } java_type_t;
 
-
 /**
  * The name and type for an atom field.
  */
@@ -100,9 +99,11 @@
  * Gather the information about the atoms.  Returns the number of errors.
  */
 int collate_atoms(const Descriptor* descriptor, Atoms* atoms);
+int collate_atom(const Descriptor *atom, AtomDecl *atomDecl,
+                 vector<java_type_t> *signature);
 
 }  // namespace stats_log_api_gen
 }  // namespace android
 
 
-#endif // ANDROID_STATS_LOG_API_GEN_COLLATION_H
+#endif // ANDROID_STATS_LOG_API_GEN_COLLATION_H
\ No newline at end of file
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 423d028..5e93c08 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -24,8 +24,6 @@
 
 using android::os::statsd::Atom;
 
-// TODO: Support WorkSources
-
 /**
  * Turn lower and camel case into upper case with underscores.
  */
@@ -97,13 +95,13 @@
     }
 }
 
-static int
-write_stats_log_cpp(FILE* out, const Atoms& atoms)
-{
+static int write_stats_log_cpp(FILE *out, const Atoms &atoms,
+                               const AtomDecl &attributionDecl) {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
     fprintf(out, "\n");
 
+    fprintf(out, "#include <exception>\n");
     fprintf(out, "#include <log/log_event_list.h>\n");
     fprintf(out, "#include <log/log.h>\n");
     fprintf(out, "#include <statslog.h>\n");
@@ -117,15 +115,29 @@
     // Print write methods
     fprintf(out, "\n");
     for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
-            signature != atoms.signatures.end(); signature++) {
+        signature != atoms.signatures.end(); signature++) {
         int argIndex;
 
         fprintf(out, "void\n");
         fprintf(out, "stats_write(int32_t code");
         argIndex = 1;
         for (vector<java_type_t>::const_iterator arg = signature->begin();
-                arg != signature->end(); arg++) {
-            fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+            arg != signature->end(); arg++) {
+            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (auto chainField : attributionDecl.fields) {
+                    if (chainField.javaType == JAVA_TYPE_STRING) {
+                            fprintf(out, ", const std::vector<%s>& %s",
+                                 cpp_type_name(chainField.javaType),
+                                 chainField.name.c_str());
+                    } else {
+                            fprintf(out, ", const %s* %s, size_t %s_length",
+                                 cpp_type_name(chainField.javaType),
+                                 chainField.name.c_str(), chainField.name.c_str());
+                    }
+                }
+            } else {
+                fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+            }
             argIndex++;
         }
         fprintf(out, ")\n");
@@ -133,15 +145,40 @@
         fprintf(out, "{\n");
         argIndex = 1;
         fprintf(out, "    android_log_event_list event(kStatsEventTag);\n");
-        fprintf(out, "    event << code;\n");
+        fprintf(out, "    event << code;\n\n");
         for (vector<java_type_t>::const_iterator arg = signature->begin();
-                arg != signature->end(); arg++) {
-            if (*arg == JAVA_TYPE_STRING) {
-                fprintf(out, "    if (arg%d == NULL) {\n", argIndex);
-                fprintf(out, "        arg%d = \"\";\n", argIndex);
+            arg != signature->end(); arg++) {
+            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (const auto &chainField : attributionDecl.fields) {
+                    if (chainField.javaType == JAVA_TYPE_STRING) {
+                        fprintf(out, "    if (%s_length != %s.size()) {\n",
+                            attributionDecl.fields.front().name.c_str(), chainField.name.c_str());
+                        fprintf(out, "        throw std::invalid_argument(\"attribution fields with"
+                            " diff length: %s vs %s\");\n",
+                            attributionDecl.fields.front().name.c_str(),
+                            chainField.name.c_str());
+                        fprintf(out, "        return;\n");
+                        fprintf(out, "    }\n");
+                    }
+                }
+                fprintf(out, "\n    event.begin();\n");
+                fprintf(out, "    for (size_t i = 0; i < %s_length; ++i) {\n",
+                    attributionDecl.fields.front().name.c_str());
+                fprintf(out, "        event.begin();\n");
+                for (const auto &chainField : attributionDecl.fields) {
+                    fprintf(out, "        event << %s[i];\n", chainField.name.c_str());
+                }
+                fprintf(out, "        event.end();\n");
                 fprintf(out, "    }\n");
+                fprintf(out, "    event.end();\n\n");
+            } else {
+                if (*arg == JAVA_TYPE_STRING) {
+                    fprintf(out, "    if (arg%d == NULL) {\n", argIndex);
+                    fprintf(out, "        arg%d = \"\";\n", argIndex);
+                    fprintf(out, "    }\n");
+                }
+                fprintf(out, "    event << arg%d;\n", argIndex);
             }
-            fprintf(out, "    event << arg%d;\n", argIndex);
             argIndex++;
         }
 
@@ -160,7 +197,7 @@
 
 
 static int
-write_stats_log_header(FILE* out, const Atoms& atoms)
+write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
 {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
@@ -168,6 +205,7 @@
     fprintf(out, "#pragma once\n");
     fprintf(out, "\n");
     fprintf(out, "#include <stdint.h>\n");
+    fprintf(out, "#include <vector>\n");
     fprintf(out, "\n");
 
     fprintf(out, "namespace android {\n");
@@ -185,7 +223,7 @@
     size_t i = 0;
     // Print constants
     for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-            atom != atoms.decls.end(); atom++) {
+        atom != atoms.decls.end(); atom++) {
         string constant = make_constant_name(atom->name);
         fprintf(out, "\n");
         fprintf(out, "    /**\n");
@@ -193,7 +231,21 @@
         fprintf(out, "     * Usage: stats_write(StatsLog.%s", constant.c_str());
         for (vector<AtomField>::const_iterator field = atom->fields.begin();
                 field != atom->fields.end(); field++) {
-            fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
+            if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (auto chainField : attributionDecl.fields) {
+                    if (chainField.javaType == JAVA_TYPE_STRING) {
+                        fprintf(out, ", const std::vector<%s>& %s",
+                             cpp_type_name(chainField.javaType),
+                             chainField.name.c_str());
+                    } else {
+                        fprintf(out, ", const %s* %s, size_t %s_length",
+                             cpp_type_name(chainField.javaType),
+                             chainField.name.c_str(), chainField.name.c_str());
+                    }
+                }
+            } else {
+                fprintf(out, ", %s %s", cpp_type_name(field->javaType), field->name.c_str());
+            }
         }
         fprintf(out, ");\n");
         fprintf(out, "     */\n");
@@ -208,8 +260,7 @@
     fprintf(out, "};\n");
     fprintf(out, "\n");
 
-    fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n",
-            maxPushedAtomId);
+    fprintf(out, "const static int kMaxPushedAtomId = %d;\n\n", maxPushedAtomId);
 
     // Print write methods
     fprintf(out, "//\n");
@@ -220,8 +271,21 @@
         fprintf(out, "void stats_write(int32_t code ");
         int argIndex = 1;
         for (vector<java_type_t>::const_iterator arg = signature->begin();
-                arg != signature->end(); arg++) {
-            fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+            arg != signature->end(); arg++) {
+            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (auto chainField : attributionDecl.fields) {
+                    if (chainField.javaType == JAVA_TYPE_STRING) {
+                        fprintf(out, ", const std::vector<%s>& %s",
+                            cpp_type_name(chainField.javaType), chainField.name.c_str());
+                    } else {
+                        fprintf(out, ", const %s* %s, size_t %s_length",
+                            cpp_type_name(chainField.javaType),
+                            chainField.name.c_str(), chainField.name.c_str());
+                    }
+                }
+            } else {
+                fprintf(out, ", %s arg%d", cpp_type_name(*arg), argIndex);
+            }
             argIndex++;
         }
         fprintf(out, ");\n");
@@ -235,7 +299,7 @@
 }
 
 static int
-write_stats_log_java(FILE* out, const Atoms& atoms)
+write_stats_log_java(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
 {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
@@ -247,7 +311,7 @@
     fprintf(out, " * API For logging statistics events.\n");
     fprintf(out, " * @hide\n");
     fprintf(out, " */\n");
-    fprintf(out, "public final class StatsLog {\n");
+    fprintf(out, "public class StatsLogInternal {\n");
     fprintf(out, "    // Constants for atom codes.\n");
 
     // Print constants for the atom codes.
@@ -259,8 +323,15 @@
         fprintf(out, "     * %s %s\n", atom->message.c_str(), atom->name.c_str());
         fprintf(out, "     * Usage: StatsLog.write(StatsLog.%s", constant.c_str());
         for (vector<AtomField>::const_iterator field = atom->fields.begin();
-                field != atom->fields.end(); field++) {
-            fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
+            field != atom->fields.end(); field++) {
+            if (field->javaType == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (auto chainField : attributionDecl.fields) {
+                    fprintf(out, ", %s[] %s",
+                        java_type_name(chainField.javaType), chainField.name.c_str());
+                }
+            } else {
+                fprintf(out, ", %s %s", java_type_name(field->javaType), field->name.c_str());
+            }
         }
         fprintf(out, ");\n");
         fprintf(out, "     */\n");
@@ -271,33 +342,41 @@
     // Print constants for the enum values.
     fprintf(out, "    // Constants for enum values.\n\n");
     for (set<AtomDecl>::const_iterator atom = atoms.decls.begin();
-            atom != atoms.decls.end(); atom++) {
+        atom != atoms.decls.end(); atom++) {
         for (vector<AtomField>::const_iterator field = atom->fields.begin();
-                field != atom->fields.end(); field++) {
-          if (field->javaType == JAVA_TYPE_ENUM) {
-            fprintf(out, "    // Values for %s.%s\n", atom->message.c_str(), field->name.c_str());
-            for (map<int, string>::const_iterator value = field->enumValues.begin();
-                 value != field->enumValues.end(); value++) {
-              fprintf(out, "    public static final int %s__%s__%s = %d;\n",
-                      make_constant_name(atom->message).c_str(),
-                      make_constant_name(field->name).c_str(),
-                      make_constant_name(value->second).c_str(),
-                      value->first);
+            field != atom->fields.end(); field++) {
+            if (field->javaType == JAVA_TYPE_ENUM) {
+                fprintf(out, "    // Values for %s.%s\n", atom->message.c_str(),
+                    field->name.c_str());
+                for (map<int, string>::const_iterator value = field->enumValues.begin();
+                    value != field->enumValues.end(); value++) {
+                    fprintf(out, "    public static final int %s__%s__%s = %d;\n",
+                        make_constant_name(atom->message).c_str(),
+                        make_constant_name(field->name).c_str(),
+                        make_constant_name(value->second).c_str(),
+                        value->first);
+                }
+                fprintf(out, "\n");
             }
-            fprintf(out, "\n");
-          }
         }
     }
 
     // Print write methods
     fprintf(out, "    // Write methods\n");
     for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
-            signature != atoms.signatures.end(); signature++) {
+        signature != atoms.signatures.end(); signature++) {
         fprintf(out, "    public static native void write(int code");
         int argIndex = 1;
         for (vector<java_type_t>::const_iterator arg = signature->begin();
-                arg != signature->end(); arg++) {
-            fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
+            arg != signature->end(); arg++) {
+            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (auto chainField : attributionDecl.fields) {
+                    fprintf(out, ", %s[] %s",
+                        java_type_name(chainField.javaType), chainField.name.c_str());
+                }
+            } else {
+                fprintf(out, ", %s arg%d", java_type_name(*arg), argIndex);
+            }
             argIndex++;
         }
         fprintf(out, ");\n");
@@ -330,12 +409,25 @@
     }
 }
 
+static const char*
+jni_array_type_name(java_type_t type)
+{
+    switch (type) {
+        case JAVA_TYPE_INT:
+            return "jintArray";
+        case JAVA_TYPE_STRING:
+            return "jobjectArray";
+        default:
+            return "UNKNOWN";
+    }
+}
+
 static string
 jni_function_name(const vector<java_type_t>& signature)
 {
     string result("StatsLog_write");
     for (vector<java_type_t>::const_iterator arg = signature.begin();
-            arg != signature.end(); arg++) {
+        arg != signature.end(); arg++) {
         switch (*arg) {
             case JAVA_TYPE_BOOLEAN:
                 result += "_boolean";
@@ -356,6 +448,9 @@
             case JAVA_TYPE_STRING:
                 result += "_String";
                 break;
+            case JAVA_TYPE_ATTRIBUTION_CHAIN:
+              result += "_AttributionChain";
+              break;
             default:
                 result += "_UNKNOWN";
                 break;
@@ -387,19 +482,26 @@
 }
 
 static string
-jni_function_signature(const vector<java_type_t>& signature)
+jni_function_signature(const vector<java_type_t>& signature, const AtomDecl &attributionDecl)
 {
     string result("(I");
     for (vector<java_type_t>::const_iterator arg = signature.begin();
-            arg != signature.end(); arg++) {
-        result += java_type_signature(*arg);
+        arg != signature.end(); arg++) {
+        if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+            for (auto chainField : attributionDecl.fields) {
+                result += "[";
+                result += java_type_signature(chainField.javaType);
+            }
+        } else {
+            result += java_type_signature(*arg);
+        }
     }
     result += ")V";
     return result;
 }
 
 static int
-write_stats_log_jni(FILE* out, const Atoms& atoms)
+write_stats_log_jni(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl)
 {
     // Print prelude
     fprintf(out, "// This file is autogenerated\n");
@@ -408,6 +510,8 @@
     fprintf(out, "#include <statslog.h>\n");
     fprintf(out, "\n");
     fprintf(out, "#include <nativehelper/JNIHelp.h>\n");
+    fprintf(out, "#include <nativehelper/ScopedUtfChars.h>\n");
+    fprintf(out, "#include <utils/Vector.h>\n");
     fprintf(out, "#include \"core_jni_helpers.h\"\n");
     fprintf(out, "#include \"jni.h\"\n");
     fprintf(out, "\n");
@@ -419,7 +523,7 @@
 
     // Print write methods
     for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
-            signature != atoms.signatures.end(); signature++) {
+        signature != atoms.signatures.end(); signature++) {
         int argIndex;
 
         fprintf(out, "static void\n");
@@ -428,7 +532,14 @@
         argIndex = 1;
         for (vector<java_type_t>::const_iterator arg = signature->begin();
                 arg != signature->end(); arg++) {
-            fprintf(out, ", %s arg%d", jni_type_name(*arg), argIndex);
+            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (auto chainField : attributionDecl.fields) {
+                    fprintf(out, ", %s %s", jni_array_type_name(chainField.javaType),
+                        chainField.name.c_str());
+                }
+            } else {
+                fprintf(out, ", %s arg%d", jni_type_name(*arg), argIndex);
+            }
             argIndex++;
         }
         fprintf(out, ")\n");
@@ -437,10 +548,11 @@
 
         // Prepare strings
         argIndex = 1;
-        bool hadString = false;
+        bool hadStringOrChain = false;
         for (vector<java_type_t>::const_iterator arg = signature->begin();
                 arg != signature->end(); arg++) {
             if (*arg == JAVA_TYPE_STRING) {
+                hadStringOrChain = true;
                 fprintf(out, "    const char* str%d;\n", argIndex);
                 fprintf(out, "    if (arg%d != NULL) {\n", argIndex);
                 fprintf(out, "        str%d = env->GetStringUTFChars(arg%d, NULL);\n",
@@ -448,13 +560,52 @@
                 fprintf(out, "    } else {\n");
                 fprintf(out, "        str%d = NULL;\n", argIndex);
                 fprintf(out, "    }\n");
-                hadString = true;
+            } else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                hadStringOrChain = true;
+                for (auto chainField : attributionDecl.fields) {
+                    fprintf(out, "    size_t %s_length = env->GetArrayLength(%s);\n",
+                        chainField.name.c_str(), chainField.name.c_str());
+                    if (chainField.name != attributionDecl.fields.front().name) {
+                        fprintf(out, "    if (%s_length != %s_length) {\n",
+                            chainField.name.c_str(),
+                            attributionDecl.fields.front().name.c_str());
+                        fprintf(out, "        jniThrowException(env, "
+                            "\"java/lang/IllegalArgumentException\", "
+                            "\"invalid attribution field(%s) length.\");\n",
+                            chainField.name.c_str());
+                        fprintf(out, "        return;\n");
+                        fprintf(out, "    }\n");
+                    }
+                    if (chainField.javaType == JAVA_TYPE_INT) {
+                        fprintf(out, "    jint* %s_array = env->GetIntArrayElements(%s, NULL);\n",
+                            chainField.name.c_str(), chainField.name.c_str());
+                    } else if (chainField.javaType == JAVA_TYPE_STRING) {
+                        fprintf(out, "    std::vector<%s> %s_vec;\n",
+                            cpp_type_name(chainField.javaType), chainField.name.c_str());
+                        fprintf(out, "    std::vector<ScopedUtfChars*> scoped_%s_vec;\n",
+                            chainField.name.c_str());
+                        fprintf(out, "    for (size_t i = 0; i < %s_length; ++i) {\n",
+                            chainField.name.c_str());
+                        fprintf(out, "        jstring jstr = "
+                            "(jstring)env->GetObjectArrayElement(%s, i);\n",
+                             chainField.name.c_str());
+                        fprintf(out, "        ScopedUtfChars* scoped_%s = "
+                            "new ScopedUtfChars(env, jstr);\n",
+                             chainField.name.c_str());
+                        fprintf(out, "        %s_vec.push_back(scoped_%s->c_str());\n",
+                                chainField.name.c_str(), chainField.name.c_str());
+                        fprintf(out, "        scoped_%s_vec.push_back(scoped_%s);\n",
+                                chainField.name.c_str(), chainField.name.c_str());
+                        fprintf(out, "    }\n");
+                    }
+                    fprintf(out, "\n");
+                }
             }
             argIndex++;
         }
-
-        // Emit this to quiet the unused parameter warning if there were no strings.
-        if (!hadString) {
+        // Emit this to quiet the unused parameter warning if there were no strings or attribution
+        // chains.
+        if (!hadStringOrChain) {
             fprintf(out, "    (void)env;\n");
         }
 
@@ -463,11 +614,24 @@
         fprintf(out, "    android::util::stats_write(code");
         for (vector<java_type_t>::const_iterator arg = signature->begin();
                 arg != signature->end(); arg++) {
-            const char* argName = (*arg == JAVA_TYPE_STRING) ? "str" : "arg";
-            fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
+            if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (auto chainField : attributionDecl.fields) {
+                    if (chainField.javaType == JAVA_TYPE_INT) {
+                        fprintf(out, ", (const %s*)%s_array, %s_length",
+                            cpp_type_name(chainField.javaType),
+                            chainField.name.c_str(), chainField.name.c_str());
+                    } else if (chainField.javaType == JAVA_TYPE_STRING) {
+                        fprintf(out, ", %s_vec", chainField.name.c_str());
+                    }
+                }
+            } else {
+                const char *argName = (*arg == JAVA_TYPE_STRING) ? "str" : "arg";
+                fprintf(out, ", (%s)%s%d", cpp_type_name(*arg), argName, argIndex);
+            }
             argIndex++;
         }
         fprintf(out, ");\n");
+        fprintf(out, "\n");
 
         // Clean up strings
         argIndex = 1;
@@ -478,6 +642,18 @@
                 fprintf(out, "        env->ReleaseStringUTFChars(arg%d, str%d);\n",
                         argIndex, argIndex);
                 fprintf(out, "    }\n");
+            } else if (*arg == JAVA_TYPE_ATTRIBUTION_CHAIN) {
+                for (auto chainField : attributionDecl.fields) {
+                    if (chainField.javaType == JAVA_TYPE_INT) {
+                        fprintf(out, "    env->ReleaseIntArrayElements(%s, %s_array, 0);\n",
+                            chainField.name.c_str(), chainField.name.c_str());
+                    } else if (chainField.javaType == JAVA_TYPE_STRING) {
+                        fprintf(out, "    for (size_t i = 0; i < %s_length; ++i) {\n",
+                            chainField.name.c_str());
+                        fprintf(out, "        delete scoped_%s_vec[i];\n", chainField.name.c_str());
+                        fprintf(out, "    }\n");
+                    }
+                }
             }
             argIndex++;
         }
@@ -494,8 +670,8 @@
     for (set<vector<java_type_t>>::const_iterator signature = atoms.signatures.begin();
             signature != atoms.signatures.end(); signature++) {
         fprintf(out, "    { \"write\", \"%s\", (void*)%s },\n",
-                jni_function_signature(*signature).c_str(),
-                jni_function_name(*signature).c_str());
+            jni_function_signature(*signature, attributionDecl).c_str(),
+            jni_function_name(*signature).c_str());
     }
     fprintf(out, "};\n");
     fprintf(out, "\n");
@@ -591,6 +767,11 @@
         return 1;
     }
 
+    AtomDecl attributionDecl;
+    vector<java_type_t> attributionSignature;
+    collate_atom(android::os::statsd::Attribution::descriptor(),
+                 &attributionDecl, &attributionSignature);
+
     // Write the .cpp file
     if (cppFilename.size() != 0) {
         FILE* out = fopen(cppFilename.c_str(), "w");
@@ -598,7 +779,8 @@
             fprintf(stderr, "Unable to open file for write: %s\n", cppFilename.c_str());
             return 1;
         }
-        errorCount = android::stats_log_api_gen::write_stats_log_cpp(out, atoms);
+        errorCount = android::stats_log_api_gen::write_stats_log_cpp(
+            out, atoms, attributionDecl);
         fclose(out);
     }
 
@@ -609,7 +791,8 @@
             fprintf(stderr, "Unable to open file for write: %s\n", headerFilename.c_str());
             return 1;
         }
-        errorCount = android::stats_log_api_gen::write_stats_log_header(out, atoms);
+        errorCount = android::stats_log_api_gen::write_stats_log_header(
+            out, atoms, attributionDecl);
         fclose(out);
     }
 
@@ -620,7 +803,8 @@
             fprintf(stderr, "Unable to open file for write: %s\n", javaFilename.c_str());
             return 1;
         }
-        errorCount = android::stats_log_api_gen::write_stats_log_java(out, atoms);
+        errorCount = android::stats_log_api_gen::write_stats_log_java(
+            out, atoms, attributionDecl);
         fclose(out);
     }
 
@@ -631,7 +815,8 @@
             fprintf(stderr, "Unable to open file for write: %s\n", jniFilename.c_str());
             return 1;
         }
-        errorCount = android::stats_log_api_gen::write_stats_log_jni(out, atoms);
+        errorCount = android::stats_log_api_gen::write_stats_log_jni(
+            out, atoms, attributionDecl);
         fclose(out);
     }
 
@@ -650,4 +835,4 @@
     GOOGLE_PROTOBUF_VERIFY_VERSION;
 
     return android::stats_log_api_gen::run(argc, argv);
-}
+}
\ No newline at end of file
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
index 6686158..84b22cda 100644
--- a/tools/stats_log_api_gen/test.proto
+++ b/tools/stats_log_api_gen/test.proto
@@ -39,22 +39,22 @@
 }
 
 message AllTypesAtom {
-    optional android.os.statsd.WorkSource attribution = 1;
-    optional double double_field = 2;
-    optional float float_field = 3;
-    optional int64 int64_field = 4;
-    optional uint64 uint64_field = 5;
-    optional int32 int32_field = 6;
-    optional fixed64 fixed64_field = 7;
-    optional fixed32 fixed32_field = 8;
-    optional bool bool_field = 9;
-    optional string string_field = 10;
-    optional uint32 uint32_field = 11;
-    optional AnEnum enum_field = 12;
-    optional sfixed32 sfixed32_field = 13;
-    optional sfixed64 sfixed64_field = 14;
-    optional sint32 sint32_field = 15;
-    optional sint64 sint64_field = 16;
+  optional android.os.statsd.AttributionChain attribution_chain = 1;
+  optional double double_field = 2;
+  optional float float_field = 3;
+  optional int64 int64_field = 4;
+  optional uint64 uint64_field = 5;
+  optional int32 int32_field = 6;
+  optional fixed64 fixed64_field = 7;
+  optional fixed32 fixed32_field = 8;
+  optional bool bool_field = 9;
+  optional string string_field = 10;
+  optional uint32 uint32_field = 11;
+  optional AnEnum enum_field = 12;
+  optional sfixed32 sfixed32_field = 13;
+  optional sfixed64 sfixed64_field = 14;
+  optional sint32 sint32_field = 15;
+  optional sint64 sint64_field = 16;
 }
 
 message Event {
@@ -99,14 +99,12 @@
     }
 }
 
-message BadWorkSourcePositionAtom {
-    optional int32 field1 = 1;
-    optional android.os.statsd.WorkSource attribution = 2;
+message BadAttributionChainPositionAtom {
+  optional int32 field1 = 1;
+  optional android.os.statsd.AttributionChain attribution = 2;
 }
 
-message BadWorkSourcePosition {
-    oneof event {
-        BadWorkSourcePositionAtom bad = 1;
-    }
+message BadAttributionChainPosition {
+  oneof event { BadAttributionChainPositionAtom bad = 1; }
 }
 
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
index 073f2cf..b2b7298 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -64,7 +64,7 @@
         } \
     } while(0)
 
-/** Expects that exactly one specific field has expected enum values. */ 
+/** Expects that exactly one specific field has expected enum values. */
 #define EXPECT_HAS_ENUM_FIELD(atom, field_name, values)        \
     do { \
         for (vector<AtomField>::const_iterator field = atom->fields.begin(); \
@@ -95,24 +95,25 @@
     EXPECT_SET_CONTAINS_SIGNATURE(atoms.signatures, JAVA_TYPE_INT, JAVA_TYPE_INT);
 
     // AllTypesAtom
-    EXPECT_SET_CONTAINS_SIGNATURE(atoms.signatures,
-                JAVA_TYPE_WORK_SOURCE, // WorkSource
-                JAVA_TYPE_DOUBLE, // double
-                JAVA_TYPE_FLOAT, // float
-                JAVA_TYPE_LONG, // int64
-                JAVA_TYPE_LONG, // uint64
-                JAVA_TYPE_INT, // int32
-                JAVA_TYPE_LONG, // fixed64
-                JAVA_TYPE_INT, // fixed32
-                JAVA_TYPE_BOOLEAN, // bool
-                JAVA_TYPE_STRING, // string
-                JAVA_TYPE_INT, // uint32
-                JAVA_TYPE_INT, // AnEnum
-                JAVA_TYPE_INT, // sfixed32
-                JAVA_TYPE_LONG, // sfixed64
-                JAVA_TYPE_INT, // sint32
-                JAVA_TYPE_LONG // sint64
-            );
+    EXPECT_SET_CONTAINS_SIGNATURE(
+        atoms.signatures,
+        JAVA_TYPE_ATTRIBUTION_CHAIN, // AttributionChain
+        JAVA_TYPE_DOUBLE,            // double
+        JAVA_TYPE_FLOAT,             // float
+        JAVA_TYPE_LONG,              // int64
+        JAVA_TYPE_LONG,              // uint64
+        JAVA_TYPE_INT,               // int32
+        JAVA_TYPE_LONG,              // fixed64
+        JAVA_TYPE_INT,               // fixed32
+        JAVA_TYPE_BOOLEAN,           // bool
+        JAVA_TYPE_STRING,            // string
+        JAVA_TYPE_INT,               // uint32
+        JAVA_TYPE_INT,               // AnEnum
+        JAVA_TYPE_INT,               // sfixed32
+        JAVA_TYPE_LONG,              // sfixed64
+        JAVA_TYPE_INT,               // sint32
+        JAVA_TYPE_LONG               // sint64
+    );
 
     set<AtomDecl>::const_iterator atom = atoms.decls.begin();
     EXPECT_EQ(1, atom->code);
@@ -187,14 +188,16 @@
 }
 
 /**
- * Test that atoms that have a WorkSource not in the first position are rejected.
+ * Test that atoms that have an attribution chain not in the first position are
+ * rejected.
  */
-TEST(CollationTest, FailBadWorkSourcePosition) {
-    Atoms atoms;
-    int errorCount = collate_atoms(BadWorkSourcePosition::descriptor(), &atoms);
+TEST(CollationTest, FailBadAttributionChainPosition) {
+  Atoms atoms;
+  int errorCount =
+      collate_atoms(BadAttributionChainPosition::descriptor(), &atoms);
 
-    EXPECT_EQ(1, errorCount);
+  EXPECT_EQ(1, errorCount);
 }
 
 }  // namespace stats_log_api_gen
-}  // namespace android
+}  // namespace android
\ No newline at end of file